Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p><strong>Freehand brush strokes using the Command Pattern</strong></p> <p>You made a good choice in implementing the “command pattern” to track your user’s freestyle brush strokes!</p> <p>Every drawing between mouseDown and mouseUp is treated as a "drag group". </p> <p>Every "drag group" is added to a master array (CommandStack[]).</p> <p>Then you can easily UNDO the last drawing by simply removing the last group on CommandStack[].</p> <p>This is what happens during a drag cycle by the user:</p> <p>MouseDown: </p> <ul> <li>Set the starting X,Y for this set of dragged lines.</li> <li>Create a new array of points dedicated to this set of drag lines (newPoints[])</li> </ul> <p>MouseMove:</p> <ul> <li>Add each mouse position point to newPoints[].</li> </ul> <p>MouseUp: </p> <ul> <li>The drag is over--Stop adding points to newPoints[].</li> <li>Store both the starting X,Y and newPoints[] to the CommandStack[] array.</li> </ul> <p>Then you can simply and efficiently UNDO strokes:</p> <ul> <li>Remove the last newPoints[] from CommandStack[] like this: CommandStack.pop()</li> <li>Redraw all the remaining strokes in CommandStack[].</li> <li>The drawing is visually the same as before the user's last stroke (quick+efficient)!</li> <li>You can remove more lines by doing more pops off the CommandStack[].</li> <li>You can also easily implement REDO by saving the newPoints[] that were popped off.</li> </ul> <p>Here is code and a Fiddle: <a href="http://jsfiddle.net/m1erickson/nUbzS/" rel="nofollow">http://jsfiddle.net/m1erickson/nUbzS/</a></p> <pre><code>&lt;!doctype html&gt; &lt;html&gt; &lt;head&gt; &lt;link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /&gt; &lt;!-- reset css --&gt; &lt;script type="text/javascript" src="http://code.jquery.com/jquery.min.js"&gt;&lt;/script&gt; &lt;!--[if lt IE 9]&gt;&lt;script type="text/javascript" src="../excanvas.js"&gt;&lt;/script&gt;&lt;![endif]--&gt; &lt;style&gt; body{ background-color: ivory; } canvas{border:1px solid red;} &lt;/style&gt; &lt;script&gt; $(function(){ var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); var lastX; var lastY; var strokeColor="red"; var strokeWidth=2; var canMouseX; var canMouseY; var canvasOffset=$("#canvas").offset(); var offsetX=canvasOffset.left; var offsetY=canvasOffset.top; var isMouseDown=false; // command pattern -- undo var commandStack=new Array(); var newStart; var newPoints=new Array(); function handleMouseDown(e){ canMouseX=parseInt(e.clientX-offsetX); canMouseY=parseInt(e.clientY-offsetY); $("#downlog").html("Down: "+ canMouseX + " / " + canMouseY); // Put your mousedown stuff here lastX=canMouseX; lastY=canMouseY; isMouseDown=true; // command pattern stuff newStart={x:canMouseX,y:canMouseY}; newPoints=new Array(); } function handleMouseUp(e){ canMouseX=parseInt(e.clientX-offsetX); canMouseY=parseInt(e.clientY-offsetY); $("#uplog").html("Up: "+ canMouseX + " / " + canMouseY); // Put your mouseup stuff here isMouseDown=false; // command pattern stuff commandStack.push({moveTo:newStart,points:newPoints}); } function handleMouseOut(e){ canMouseX=parseInt(e.clientX-offsetX); canMouseY=parseInt(e.clientY-offsetY); $("#outlog").html("Out: "+ canMouseX + " / " + canMouseY); // Put your mouseOut stuff here isMouseDown=false; } function handleMouseMove(e){ canMouseX=parseInt(e.clientX-offsetX); canMouseY=parseInt(e.clientY-offsetY); $("#movelog").html("Move: "+ canMouseX + " / " + canMouseY); // Put your mousemove stuff here if(isMouseDown){ ctx.beginPath(); ctx.moveTo(lastX,lastY); ctx.lineTo(canMouseX,canMouseY); ctx.stroke(); lastX=canMouseX; lastY=canMouseY; // command pattern stuff newPoints.push({x:canMouseX,y:canMouseY}); } } $("#canvas").mousedown(function(e){handleMouseDown(e);}); $("#canvas").mousemove(function(e){handleMouseMove(e);}); $("#canvas").mouseup(function(e){handleMouseUp(e);}); $("#canvas").mouseout(function(e){handleMouseOut(e);}); $("#undo").click(function(e){ undoLast(); }); function undoLast(){ commandStack.pop(); redrawAll(); } function redrawAll(){ // prep for commandStack redraws ctx.clearRect(0,0,canvas.width,canvas.height); ctx.save() ctx.strokeStyle="blue"; ctx.beginPath(); // loop through the commandStack and draw all nodes for(var s=0;s&lt;commandStack.length;s++){ // move to the starting point of this node var start=commandStack[s].moveTo; ctx.moveTo(start.x,start.y); // draw each line segment in this node var pts=commandStack[s].points; for(var p=0;p&lt;pts.length;p++){ ctx.lineTo(pts[p].x,pts[p].y); } // end for(p) } // end for(s) // actually draw the lines ctx.stroke(); ctx.restore(); } }); // end $(function(){}); &lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;p id="downlog"&gt;Down&lt;/p&gt; &lt;p id="movelog"&gt;Move&lt;/p&gt; &lt;p id="uplog"&gt;Up&lt;/p&gt; &lt;p id="outlog"&gt;Out&lt;/p&gt; &lt;canvas id="canvas" width=300 height=300&gt;&lt;/canvas&gt; &lt;button id="undo"&gt;Undo&lt;/button&gt; &lt;/body&gt; &lt;/html&gt; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload