コマンド パターンを使用したフリーハンド ブラシ ストローク
ユーザーのフリースタイル ブラシ ストロークを追跡するための「コマンド パターン」の実装は、適切な選択でした。
mouseDown と mouseUp の間のすべての描画は、「ドラッグ グループ」として扱われます。
すべての「ドラッグ グループ」は、マスター配列 (CommandStack[]) に追加されます。
次に、CommandStack[] の最後のグループを削除するだけで、最後の描画を簡単に元に戻すことができます。
これは、ユーザーによるドラッグ サイクル中に発生することです。
マウスダウン:
- このドラッグされた線のセットの開始 X、Y を設定します。
- この一連のドラッグ ライン専用のポイントの新しい配列を作成します (newPoints[])
マウス移動:
- 各マウス位置ポイントを newPoints[] に追加します。
マウスアップ:
- ドラッグは終わりです -- newPoints[] へのポイントの追加を停止します。
- 開始 X、Y および newPoints[] の両方を CommandStack[] 配列に格納します。
次に、ストロークを簡単かつ効率的に元に戻すことができます。
- 次のように CommandStack[] から最後の newPoints[] を削除します: CommandStack.pop()
- CommandStack[] の残りのすべてのストロークを再描画します。
- 描画は、ユーザーが最後のストロークを行う前と視覚的に同じです (迅速かつ効率的)。
- CommandStack[] からさらに pop を実行することで、さらに多くの行を削除できます。
- ポップされた newPoints[] を保存することで、簡単に REDO を実装することもできます。
ここにコードとフィドルがあります: http://jsfiddle.net/m1erickson/nUbzS/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<!--[if lt IE 9]><script type="text/javascript" src="../excanvas.js"></script><![endif]-->
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(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<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<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(){});
</script>
</head>
<body>
<p id="downlog">Down</p>
<p id="movelog">Move</p>
<p id="uplog">Up</p>
<p id="outlog">Out</p>
<canvas id="canvas" width=300 height=300></canvas>
<button id="undo">Undo</button>
</body>
</html>