2013-03-16 83 views
0

我有一個HTML5畫布,允許用戶繪製各種形狀和畫筆功能,允許用戶徒手畫。我正在使用這些實現的命令模式。我面臨的問題是「撤消」功能。它適用於所有其他命令,但在涉及「刷」時似乎存在問題。被覆蓋的HTML5畫布圖紙

畫筆的工作方式是每次拖動鼠標時存儲點,一旦添加了一個新點,整個點的陣列就會重新繪製在屏幕上。只有當用戶釋放鼠標時,繪圖纔會停止。您可能會立即看到問題,點越大,他們在屏幕上重繪得越多。這使得線條顏色看起來比實際顏色更深。

我的解決方案是隻將最後一個點n-1與點n-2連接起來,但實際上只會重繪最後2個點。我不完全理解帆布如何工作以及爲什麼這種方法不起作用,但是重繪帆布點似乎很有效......

下面是一些代碼,概述了關鍵部分。

BrushStrat.prototype.mousemove=function(event){ 
    if(this.command!=null){ 
      //add this point to the list 
     this.command.addPoint({x:event.canvasX, y:event.canvasY}); 
      //redraw all points 
     this.command.draw(this.paint.context); 
    } 
} 

BrushCommand.prototype.draw=function(context){ 
    if(this.points.length==0){ 
     return; 
    } 

    context.beginPath(); 
    context.strokeStyle = this.strokeStyle; 
    context.lineWidth=this.lineWidth; 
    //start from the first point 
    context.moveTo(this.points[0].x,this.points[0].y); 
    //redraw all subsequent points 
    for(var i=1;i<this.points.length;i++){ 
     context.lineTo(this.points[i].x, this.points[i].y); 
    } 
    context.stroke(); 
} 

回答

1

寫意刷使用命令模式

您在執行「命令模式」來跟蹤用戶的自由泳筆觸取得一個不錯的選擇招!

mouseDown和mouseUp之間的每個圖形都被視爲「拖動組」。

每個「拖動組」都添加到主控陣列(CommandStack [])中。

然後,您可以通過簡單地刪除CommandStack []上的最後一組,輕鬆地撤消最後一個繪圖。

這是一拖週期由用戶時會發生什麼:

的MouseDown:

  • 將啓動X,Y爲這套拖行。
  • 創建專用於該組的拖動線(newPoints [])

的MouseMove點的一個新的數組:

  • 每個鼠標的位置點添加到newPoints []。

的MouseUp:

  • 拖拽結束 - 停止加分newP​​oints []。
  • 將起始X,Y和newPoints []存儲到CommandStack []數組中。

然後你就可以簡單有效地UNDO招:

  • 刪除最後newPoints []從CommandStack []是這樣的:CommandStack.pop()
  • 重繪所有剩餘的筆劃CommandStack [ ]。
  • 該圖形在視覺上與用戶上一次中風(快速+高效)之前相同!
  • 您可以通過更多地從CommandStack []中彈出來刪除更多行。
  • 您還可以通過保存已彈出的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> 
+0

真棒,這是我實現它的方式這個問題似乎神奇地消失了:所以它的一切都好! – 1337holiday 2013-03-17 01:57:40

2

在重繪每個數組點之前清除畫布或至少繪圖區域。

編輯 對不起,我以爲你有一個遊戲循環。但是,它仍然是一個有效的選項:在繪圖區域使用兩個畫布。一個用於繪製「當前」形狀/波形(在繪製每個點之前清除)和另一個具有所有完成形狀/波形的持久層。所以要回顧一下,當用戶點擊並拖動這個形狀時,它會被繪製到當前圖層。當用戶釋放鼠標時,圖像現在被「鎖定」並轉移到持久層。

希望是有道理的。

+0

但如果我清除畫布不會影響當前屏幕上的其他圖像?我想我可以添加點,清除畫布,重新繪製每個項目,然後重新繪製線上的所有點。但是,這將是非常有效的:( – 1337holiday 2013-03-16 20:26:39

+0

這是一個好主意,如果我再次遇到這個問題,我可以試試看,這也會使撤消更有效率,我想。 – 1337holiday 2013-03-17 01:58:28