Skip to content

Instantly share code, notes, and snippets.

@NSDesign
Created September 10, 2014 14:19
Show Gist options
  • Select an option

  • Save NSDesign/85a35aa10108a59b84c2 to your computer and use it in GitHub Desktop.

Select an option

Save NSDesign/85a35aa10108a59b84c2 to your computer and use it in GitHub Desktop.
A Pen by Rich East.
<!-- 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">&nbsp</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>
// ************ 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);
};
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