画布沿着路径绘制

时间:2015-08-02 09:45:30

标签: javascript html5 animation canvas

我想在HTML5 canvas /或SVG上执行以下操作:

  1. 拥有背景路径,将光标移动并绘制(填充)背景路径
  2. 用户完成绘图后具有回调功能
  3. enter image description here

    我的问题是我不知道如何检查绘制的线是否跟随路径。

    有人可以解释一下如何做到这一点或者提供一些提示吗?

    http://jsbin.com/reguyuxawo/edit?html,js,console,output

    function drawBgPath() {
      context.beginPath();
      context.moveTo(100, 20);
    
      context.lineTo(200, 160);
      context.quadraticCurveTo(230, 200, 250, 120);
      context.bezierCurveTo(290, -40, 300, 200, 400, 150);
      context.lineTo(500, 90);
      context.lineWidth = 5;
      context.strokeStyle = 'rgba(0,0,0,.2)';
      context.stroke();
    }
    

1 个答案:

答案 0 :(得分:1)

  1. 创建一个隐藏的画布,将原始路径存储为问题画布,例如#q

  2. #c

  3. 上绘制问题
  4. 当用户即将绘制时,从问题中获取像素值,看它是否在线上。

  5. 通过上述信息确定绘制颜色。

  6. 
    
    var mousePressed = false;
    var lastX, lastY;
    var ctx;
    
    var canvas = document.getElementById('c');
    var context = canvas.getContext('2d');
    var canvasq = document.getElementById('q');
    var contextq = canvasq.getContext('2d');
    
    canvas.width = 500;
    canvas.height = 500;
    canvasq.width = 500;
    canvasq.height = 500;
    
       $('#c').mousedown(function (e) {
            mousePressed = true;
            Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, false);
        });
    
        $('#c').mousemove(function (e) {
            if (mousePressed) {
                Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, true);
            }
        });
    
        $('#c').mouseup(function (e) {
            mousePressed = false;
        });
    	    $('#c').mouseleave(function (e) {
            mousePressed = false;
        });
    
    function drawBgPath() {
      contextq.beginPath();
      contextq.moveTo(100, 20);
    
      contextq.lineTo(200, 160);
      contextq.quadraticCurveTo(230, 200, 250, 120);
      contextq.bezierCurveTo(290, -40, 300, 200, 400, 150);
      contextq.lineTo(500, 90);
      contextq.lineWidth = 5;
      contextq.strokeStyle = 'rgba(0,0,0,.2)';
      contextq.stroke();
      context.drawImage(canvasq, 0, 0);
    }
    
    function Draw(x, y, isDown) {
        // If not integer, getImageData will get a 2x2 region.
        x = Math.round(x);
        y = Math.round(y);
        if (isDown) {
            var pixel = contextq.getImageData(x, y, 1, 1);
            // If the canvas is not draw by line, the opacity value will be 0.
            var color = (pixel.data[3] === 0) ? 'red' : 'purple';
            context.beginPath();
            context.strokeStyle = color;
            context.lineWidth = 5;
            context.lineJoin = "round";
            context.moveTo(lastX, lastY);
            context.lineTo(x, y);
            context.closePath();
            context.stroke();
        }
        lastX = x; lastY = y;
    }
    
    drawBgPath();
    Draw();
    
    #q {
      display: none;
    }
    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
    <canvas id="c"></canvas>
    <canvas id="q"></canvas>
    &#13;
    &#13;
    &#13;

    另一种方式是:

    1. 创建另外2个画布,用于回答和提问。

    2. 当鼠标按下时,首先在回答中绘制路径。

    3. 然后将答案画布与问题画布进行比较。

    4. 在画布上绘制比较答案以显示。

    5. 我只是演示如何在这里实现。您可以剪裁绘制区域以提高性能。

      以某种方式很难确定路径是否完整。但你仍然可以:

      1. 按问题剪辑答案图片,然后逐个比较他们的像素值。

      2. 如果有问题的像素有颜色total + 1,如果两个像素的颜色和颜色相同,则为count + 1

      3. 检查count/total是否超过特定阈值。

      4. 如果图片很大,可能会很慢,所以我只想在用户mouseup或点击检查按钮时进行检查。 我还尝试使用.toDataURL来按字符串比较它们的值,但是,它太严格了,不能让你有一个阈值。

        &#13;
        &#13;
        var mousePressed = false;
        var lastX, lastY;
        var ctx;
        
        // Question part
        var qCanvas = document.createElement('canvas');
        var qContext = qCanvas.getContext('2d');
        
        var aCanvas = document.createElement('canvas');
        var aContext = aCanvas.getContext('2d');
        
        var canvas = document.getElementById('c');
        var context = canvas.getContext('2d');
        
        canvas.width = 500;
        canvas.height = 500;
        qCanvas.width = 500;
        qCanvas.height = 500;
        aCanvas.width = 500;
        aCanvas.height = 500;
        
           $('#c').mousedown(function (e) {
                mousePressed = true;
                Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, false);
            });
        
            $('#c').mousemove(function (e) {
                if (mousePressed) {
                    Draw(e.pageX - $(this).offset().left, e.pageY - $(this).offset().top, true);
                }
            });
        
            $('#c').mouseup(function (e) {
                mousePressed = false;
            });
        	    $('#c').mouseleave(function (e) {
                mousePressed = false;
            });
        
        function drawBgPath() {
          qContext.beginPath();
          qContext.moveTo(100, 20);
        
          qContext.lineTo(200, 160);
          qContext.quadraticCurveTo(230, 200, 250, 120);
          qContext.bezierCurveTo(290, -40, 300, 200, 400, 150);
          qContext.lineTo(500, 90);
          qContext.lineWidth = 5;
          qContext.strokeStyle = 'rgb(0,0,0)';
          qContext.stroke();
          
          // Draw Question on canvas
          context.save();
          context.globalAlpha = 0.2;
          context.drawImage(qCanvas, 0, 0);
          context.restore();
          
           // Now fill the question with purple.
          qContext.fillStyle = 'purple';
          qContext.globalCompositeOperation = 'source-atop';
          qContext.fillRect(0, 0, qCanvas.width, qCanvas.height);
        }
        
        function Draw(x, y, isDown) {
            if (isDown) {
                // First draw on answer canvas
                aContext.beginPath();
                aContext.strokeStyle = 'red';
               console.log(x, y);
                aContext.lineWidth = 5;
                aContext.lineJoin = "round";
                aContext.moveTo(lastX, lastY);
                aContext.lineTo(x, y);
                aContext.closePath();
                aContext.stroke();
              
                // Compare answer with question.
                aContext.save();
                aContext.globalCompositeOperation = 'source-atop';
                aContext.drawImage(qCanvas, 0, 0);
                aContext.restore();
                
                // Draw the result on what you want to show.
                context.drawImage(aCanvas, 0, 0);
            }
            lastX = x; lastY = y;
        }
        
        var cv = document.createElement('canvas');
        cv.width = 500;
        cv.height = 500;
        //document.body.appendChild(cv);
        var ctx = cv.getContext('2d');
        function checkAnswer() {
          cv.width = 500;
          cv.height = 500;
          ctx.globalCompositeOperation = 'source-over';
          ctx.drawImage(aCanvas, 0, 0);
          ctx.globalCompositeOperation = 'destination-in';
          ctx.drawImage(qCanvas, 0, 0);
          var qData = qContext.getImageData(0, 0, 500, 500).data;
          var aData = ctx.getImageData(0, 0, 500, 500).data;
          var idx = 0, i, j;
          var count = 0, total = 0;
          for (i = 0; i < 500; ++i) {
            for (j = 0; j < 500; ++j) {
              if (qData[idx] !== 0) {
                ++total;
                if (aData[idx] === qData[idx]) {
                  ++count;
                }
              }
              idx += 4;
            }
          }
          console.log(count,total);
          // Threshold.
          if (count/total > 0.95) {
            alert('Complete');
          }
        }
        
        drawBgPath();
        Draw();
        &#13;
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
        <canvas id="c"></canvas>
        <button onclick="checkAnswer()">check</button>
        &#13;
        &#13;
        &#13;