Created
September 10, 2014 14:19
-
-
Save NSDesign/85a35aa10108a59b84c2 to your computer and use it in GitHub Desktop.
A Pen by Rich East.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- MOVE NOT ADDED YET. OTHER BUTTONS SHOULD WORK --> | |
| <div class="footer" id="footer"> | |
| <div class="btn btn-sq btn-grey" id="zoomOut">-</div> | |
| <div class="btn btn-sq btn-grey" id="zoomIn">+</div> | |
| <div class="btn spacer-8"> </div> | |
| <div class="btn btn-sm btn-blue btn-select" id="draw">Draw</div> | |
| <div class="btn btn-sm btn-blue" id="move">Move</div> | |
| <div class="btn btn-sm btn-blue" id="delete">Delete</div> | |
| <div class="btn btn-med btn-blue right" id="reset">RESET</div> | |
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ************ CUSTOMIZABLE CONFIGURATION ************ // | |
| // TODO: These should just be text boxes/color pickers | |
| var Config = { | |
| minColCount: 16, | |
| minRowCount: 48, | |
| zoomRate: 4, | |
| gridScale: 24, | |
| gridWidth: 0.4, | |
| gridColor:'rgba(0,0,0,0.5)', | |
| emptyColor: 'rgb(255,255,255)' | |
| }; | |
| // ******************* ACTUAL CODE ******************* // | |
| //-----------------------------------------------------// | |
| // Initial Setup // | |
| //-----------------------------------------------------// | |
| var Page = {body:0, cvs:0, ctx:0, footer:0, | |
| btnZoomIn:0, btnZoomOut:0, | |
| l:0, t:0, w:0, h:0, | |
| u:0, minU:12, maxU:48, | |
| colCount: 0, rowCount: 0, isDirty:0}; | |
| var Tool = {active:0, draw:0, move:0, delete:0}; | |
| var ObjActive = 0, | |
| ObjStatic = []; | |
| Load(); | |
| function Load(){ | |
| Page.body = document.getElementsByTagName('body')[0]; | |
| Page.cvs = document.createElement('canvas'); | |
| Page.ctx = Page.cvs.getContext('2d'); | |
| Page.body.appendChild(Page.cvs); | |
| Page.cvs.style.position = 'absolute'; | |
| Page.footer = document.getElementById('footer'); | |
| Page.btnZoomIn = document.getElementById('zoomIn'); | |
| Page.btnZoomOut = document.getElementById('zoomOut'); | |
| Tool.draw = document.getElementById('draw'); | |
| Tool.move = document.getElementById('move'); | |
| Tool.delete = document.getElementById('delete'); | |
| Tool.active = Tool.draw; | |
| Tool.state = 0; | |
| Page.u = Config.gridScale; | |
| //SetInputEvents(); | |
| Init(); | |
| } | |
| function Init(){ | |
| ObjActive = 0; | |
| ObjStatic = []; | |
| Page.isDirty = 1; | |
| CheckWindowSize(); | |
| UpdateLoop(); | |
| } | |
| function CheckWindowSize(){ | |
| var w = Page.body.offsetWidth, | |
| h = Page.body.offsetHeight - Page.footer.offsetHeight; | |
| if (w !== Page.w || h !== Page.h){ | |
| Page.cvs.width = w; | |
| Page.cvs.height = h; | |
| SetScale(w,h,Page.u); | |
| } | |
| } | |
| function SetScale(w,h,u){ | |
| Page.isDirty = 1; | |
| Page.w = (w !== null) ? w : Page.w; | |
| Page.h = (h !== null) ? h : Page.h; | |
| Page.u = (u !== null) ? u : Page.u; | |
| Page.colCount = Math.ceil(Page.w / Page.u); | |
| Page.rowCount = Math.ceil(Page.h / Page.u); | |
| Page.w; | |
| Page.cvs.height = Page.h; | |
| Page.cvs.style.left = Page.cvs.style.top = 0; | |
| } | |
| function TryZoom(dir){ | |
| if (CanZoom(dir)){ | |
| var newU = Clamp(Page.u+dir*Config.zoomRate,Page.minU,Page.maxU); | |
| if (newU === Page.minU){ | |
| AddClass(Page.btnZoomOut,'btn-inactive'); | |
| } | |
| else if (Page.u === Page.minU){ | |
| RemoveClass(Page.btnZoomOut,'btn-inactive'); | |
| } | |
| if (newU === Page.maxU){ | |
| AddClass(Page.btnZoomIn,'btn-inactive'); | |
| } | |
| else if (Page.u === Page.maxU){ | |
| RemoveClass(Page.btnZoomIn,'btn-inactive'); | |
| } | |
| SetScale(Page.w,Page.h,newU); | |
| } | |
| } | |
| function CanZoom(dir){ | |
| return ((Page.u>Page.minU&&dir<0) || | |
| (Page.u<Page.maxU&&dir>0)); | |
| } | |
| function TrySelectTool(thisTool){ | |
| if (thisTool != Tool.active){ | |
| RemoveClass(Tool.active,'btn-select'); | |
| AddClass(thisTool,'btn-select'); | |
| Tool.active = thisTool; | |
| Tool.state = GetToolState(); | |
| } | |
| } | |
| function GetToolState(){ | |
| switch (Tool.active){ | |
| case Tool.draw: return 0; | |
| case Tool.move: return 1; | |
| case Tool.delete: return 2; | |
| default: return -1; | |
| } | |
| } | |
| function DrawBackground(){ | |
| Page.ctx.fillStyle = Config.emptyColor; | |
| Page.ctx.fillRect(0, 0, Page.w, Page.h); | |
| } | |
| function DrawGrid(){ | |
| var i, len; | |
| Page.ctx.beginPath(); | |
| for(i = 1, len = Page.colCount ; i < len ; i++){ | |
| Page.ctx.moveTo(i * Page.u, 0); | |
| Page.ctx.lineTo(i * Page.u, Page.h); | |
| } | |
| for(i = 1, len = Page.rowCount ; i < len ; i++){ | |
| Page.ctx.moveTo(0, i * Page.u); | |
| Page.ctx.lineTo(Page.w, i * Page.u); | |
| } | |
| Page.ctx.lineWidth = Config.gridWidth; | |
| Page.ctx.strokeStyle = Config.gridColor; | |
| Page.ctx.stroke(); | |
| } | |
| //-----------------------------------------------------// | |
| // User Input Events // | |
| //-----------------------------------------------------// | |
| window.addEventListener('resize', CheckWindowSize, false); | |
| document.getElementById('reset').onclick = function(){ Init(); }; | |
| Page.btnZoomOut.onclick = function(){ TryZoom(-1); }; | |
| Page.btnZoomIn.onclick = function(){ TryZoom(1); }; | |
| Tool.draw.onclick = function(){TrySelectTool(Tool.draw)}; | |
| Tool.move.onclick = function(){TrySelectTool(Tool.move)}; | |
| Tool.delete.onclick = function(){TrySelectTool(Tool.delete)}; | |
| Page.cvs.onmousedown = function(evt){ | |
| CheckClick(evt); | |
| document.onmousemove = function(evt){ | |
| CheckMouseMove(evt); | |
| }; | |
| document.onmouseup = function(evt){ | |
| CheckUnclick(evt); | |
| }; | |
| }; | |
| // INPUT FUNCTIONS | |
| function CheckUnclick() { | |
| if (Tool.state === 0){ | |
| if (ObjActive !== 0 && ObjActive !== 1){ | |
| if (ObjActive.curX != ObjActive.anchorX && | |
| ObjActive.curY != ObjActive.anchorY){ | |
| ObjStatic.push(ObjActive); | |
| } | |
| ObjActive = 1; | |
| } | |
| } | |
| else if (Tool.state === 2){ | |
| } | |
| } | |
| function CheckClick(evt) { | |
| if (evt.button == 2){ | |
| alert("RIGHT CLICK"); | |
| } | |
| else if (Tool.state === 0){ | |
| var unitPos = GetMousePosUnits(evt); | |
| // TODO: null coords is pretty hacky, should return 3rd param | |
| if (unitPos.x !== null && unitPos.y !== null){ | |
| isLooping = (ObjActive === 0 || ObjActive === 1) ? true : false; | |
| ObjActive = new DrawObj(unitPos.x,unitPos.y,'rgba(96,160,192,1)'); | |
| Page.isDirty = 1; | |
| } | |
| } | |
| else if (Tool.state === 2){ | |
| TryDeleteDrawArea(evt); | |
| } | |
| } | |
| function CheckMouseMove(evt){ | |
| if(Tool.state === 0){ | |
| if (ObjActive !== 0 && ObjActive !== 1){ | |
| ObjActive.UpdateCoords(evt); | |
| } | |
| } | |
| } | |
| function GetMousePosUnits(evt) { | |
| var pixelPos = GetMousePosPixels(evt); | |
| return PixelsToUnits(pixelPos.x,pixelPos.y); | |
| } | |
| function GetMousePosPixels(evt) { | |
| var xPos = evt.clientX - Page.l, | |
| yPos = evt.clientY - Page.t; | |
| xPos = Math.min(Math.max(xPos,0),Page.w); | |
| yPos = Math.min(Math.max(yPos,0),Page.h); | |
| return {x: xPos, y: yPos}; | |
| } | |
| function PixelsToUnits(x, y){ | |
| var unitX = null, | |
| unitY = null; | |
| if (x > -10 && x <= Page.w+10 && y >= -10 && y <= Page.h+10) { | |
| unitX = Math.round(x/Page.u); | |
| unitY = Math.round(y/Page.u); | |
| } | |
| return { x: unitX, y: unitY }; | |
| } | |
| function TryDeleteDrawArea(evt){ | |
| var point = GetMousePosPixels(evt); | |
| // Search backwards from the newest | |
| for(var i = ObjStatic.length - 1; i >= 0; i--){ | |
| if (PointInRect(point,ObjStatic[i].ToPixelRect())){ | |
| ObjStatic.splice(i,1); | |
| Page.isDirty = 1; | |
| break; | |
| } | |
| } | |
| } | |
| var DrawObj = function(anchorX, anchorY, fillStyle){ | |
| this.fillStyle = fillStyle; | |
| this.anchorX = anchorX; | |
| this.anchorY = anchorY; | |
| this.curX = anchorX; | |
| this.curY = anchorY; | |
| this.UpdateCoords = function(evt){ | |
| var unitPos = GetMousePosUnits(evt); | |
| if (unitPos.x != this.curX || unitPos.y != this.curY){ | |
| this.curX = Math.min(Math.max(unitPos.x,0),Page.colCount); | |
| this.curY = Math.min(Math.max(unitPos.y,0),Page.rowCount); | |
| } | |
| }; | |
| this.ToPixelRect = function(){ | |
| var l = Page.u * Math.min(this.anchorX, this.curX), | |
| r = Page.u * Math.max(this.anchorX, this.curX), | |
| t = Page.u * Math.min(this.anchorY, this.curY), | |
| b = Page.u * Math.max(this.anchorY, this.curY); | |
| return {left:l, top:t, right:r, bottom:b} | |
| }; | |
| this.Draw = function(isActive){ | |
| var left = this.anchorX * Page.u, | |
| top = this.anchorY * Page.u, | |
| right = this.curX * Page.u, | |
| bottom = this.curY * Page.u, | |
| width = right - left, | |
| height = bottom - top; | |
| Page.ctx.fillStyle = this.fillStyle; | |
| Page.ctx.fillRect(left, top, width, height); | |
| // flashing effect for the active draw area | |
| if (isActive === 1){ | |
| var alpha = 0.2 + Math.sin(Date.now()/80)/20; | |
| Page.ctx.fillStyle = 'rgba(255,255,255,' + alpha + ')'; | |
| Page.ctx.fillRect(left, top, width, height); | |
| Page.ctx.beginPath(); | |
| Page.ctx.moveTo(left,top); | |
| } | |
| Page.ctx.lineWidth = 1.5; | |
| Page.ctx.strokeStyle = 'rgba(0,0,0,0.5)'; | |
| Page.ctx.strokeRect(left, top, width, height); | |
| // visuals on the anchor point and current point | |
| if (isActive === 1){ | |
| Page.ctx.beginPath(); | |
| Page.ctx.arc(left,top,Page.u/5,0,2*Math.PI,false); | |
| Page.ctx.arc(right,bottom,Page.u/5,0,2*Math.PI,false); | |
| Page.ctx.fillStyle = this.fillStyle; | |
| Page.ctx.fill(); | |
| Page.ctx.fillStyle = 'rgba(0,0,0,0.5)'; | |
| Page.ctx.fill(); | |
| } | |
| }; | |
| }; | |
| //-----------------------------------------------------// | |
| // Draw Update Loop // | |
| //-----------------------------------------------------// | |
| function UpdateLoop(){ | |
| if (Page.isDirty === 1){ | |
| DrawBackground(); | |
| for(var i = 0, len = ObjStatic.length; i < len; i++){ | |
| ObjStatic[i].Draw(0); | |
| } | |
| // TODO: stop being so hacky with the ObjActive 0 and 1 stuff | |
| if (ObjActive !== 0 && ObjActive !== 1){ | |
| ObjActive.Draw(1); | |
| } | |
| else{ | |
| Page.isDirty = ObjActive = 0; | |
| } | |
| DrawGrid(); | |
| } | |
| window.requestAnimationFrame(UpdateLoop); | |
| } | |
| //-----------------------------------------------------// | |
| // Helper Functions // | |
| //-----------------------------------------------------// | |
| function PointInRect(p, r){ | |
| return (p.x>=r.left&&p.x<=r.right&&p.y>=r.top&&p.y<=r.bottom); | |
| } | |
| function Clamp(n,min,max){ | |
| return Math.min(Math.max(n,min),max); | |
| } | |
| function AddClass(ele,classStr){ | |
| ele.className = ele.className.replaceAll(' '+classStr,'')+' '+classStr; | |
| } | |
| function RemoveClass(ele,classStr){ | |
| ele.className = ele.className.replaceAll(' '+classStr,''); | |
| } | |
| function ToggleClass(ele,classStr){ | |
| var str = ele.className.replaceAll(' '+classStr,''); | |
| ele.className = (str.length===ele.className.length)?str+' '+classStr:str; | |
| } | |
| String.prototype.replaceAll = function (replaceThis, withThis) { | |
| var re = new RegExp(replaceThis,"g"); | |
| return this.replace(re, withThis); | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| html{ | |
| height:100%; | |
| } | |
| body{ | |
| overflow: hidden; | |
| height:100%; | |
| min-width: 400px; | |
| margin:0; | |
| background: rgb(220,220,220); | |
| } | |
| canvas{ | |
| outline: thin solid rgb(220,220,220); | |
| } | |
| .footer{ | |
| position: absolute; | |
| overflow: hidden; | |
| min-width: 600px; | |
| left: 0; right: 0; bottom: 0; | |
| height: 2em; | |
| padding: 0.5em; | |
| background: rgb(42,46,50); | |
| vertical-align: middle; | |
| z-index: 1; | |
| cursor: default; | |
| -webkit-user-select: none; /* Chrome all / Safari all */ | |
| -moz-user-select: none; /* Firefox all */ | |
| -ms-user-select: none; /* IE 10+ */ | |
| /* No support for these yet, use at own risk */ | |
| -o-user-select: none; | |
| user-select: none; | |
| } | |
| .btn{ | |
| display: inline-block; | |
| position: relative; | |
| margin: 0 auto; | |
| height: 2em; | |
| cursor: pointer; | |
| color: white; | |
| text-align: center; | |
| font: 600 1em 'Open Sans', sans-serif; | |
| line-height: 2em; | |
| } | |
| .btn-selected{ outline: 0.25em solid rgb(64,160,192); } | |
| .btn-inactive{ cursor: default; } | |
| .btn-med{ | |
| width: 8em; | |
| } | |
| .btn-sm{ | |
| width: 6em; | |
| } | |
| .btn-sq{ | |
| width: 2em; | |
| } | |
| .btn-blue{ | |
| border: thin solid rgb(80,172,204); | |
| background: rgb(64,160,192); | |
| } | |
| .btn-blue:hover{ | |
| border: thin solid rgb(96,188,220); | |
| background: rgb(80,176,208); | |
| } | |
| .btn-blue:active{ | |
| color: rgb(240,240,240); | |
| border: thin solid rgb(72,164,196); | |
| background: rgb(56,152,184); | |
| } | |
| .btn-blue.btn-select{ | |
| cursor: default; | |
| color: rgb(255,255,255); | |
| border: thin solid rgb(40,82,100); | |
| background: rgb(36,76,92); | |
| } | |
| .btn-grey{ | |
| border: thin solid rgb(176,176,176); | |
| background: rgb(168,168,168); | |
| } | |
| .btn-grey:hover{ | |
| border: thin solid rgb(192,192,192); | |
| background: rgb(184,184,184); | |
| } | |
| .btn-grey:active{ | |
| color: rgb(240,240,240); | |
| border: thin solid rgb(168,168,168); | |
| background: rgb(160,160,160); | |
| } | |
| .btn-grey.btn-inactive{ | |
| color: rgb(128,128,128); | |
| border: thin solid rgb(88,88,88); | |
| background: rgb(80,80,80); | |
| } | |
| .spacer-1,.spacer-2,.spacer-3,.spacer-4,.spacer-12{ cursor: default; } | |
| .spacer-1{ width: 0.5em; } | |
| .spacer-2{ width: 1em; } | |
| .spacer-3{ width: 1.5em; } | |
| .spacer-4{ width: 2em; } | |
| .spacer-6{ width: 3em; } | |
| .spacer-8{ width: 4em; } | |
| .right{float:right}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment