0

ユーザーがさまざまな形状を描画できる 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();
}
4

2 に答える 2

2

各配列ポイントを再描画する前に、キャンバスをクリアするか、少なくとも描画領域をクリアしてください。

編集 うん、すみません、ゲームループがあると思いました。ただし、それでも有効なオプションです。描画領域に2つのキャンバスを使用します。1つは描画中の「現在の」形状/波線(各ポイントを描画する前にクリアします)用で、もう1つはすべての完成した形状/波線を含む永続的なレイヤーです。

つまり、要約すると、ユーザーがクリックしてドラッグすると、この図形が現在のレイヤーに描画されます。ユーザーがマウスを離すと、画像が「ロック」され、永続レイヤーに転送されます。

それが理にかなっていることを願っています。

于 2013-03-16T19:11:58.330 に答える
2

コマンド パターンを使用したフリーハンド ブラシ ストローク

ユーザーのフリースタイル ブラシ ストロークを追跡するための「コマンド パターン」の実装は、適切な選択でした。

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>
于 2013-03-16T22:52:16.683 に答える