【问题标题】:Canvas draw following the path画布跟随路径绘制
【发布时间】:2015-10-24 13:48:34
【问题描述】:

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

  1. 有一个背景路径,将光标移到并绘制(填充)背景路径
  2. 用户完成绘制后有回调函数

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

谁能解释我如何做到这一点,或者给一些提示?

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();
}

【问题讨论】:

  • “背景路径”和“跟随路径”到底是什么意思?

标签: javascript html animation canvas


【解决方案1】:
  1. 创建一个隐藏的画布,将原始路径存储为问题画布,比如#q

  2. #c上画题。

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

  4. 根据以上信息决定绘制颜色。

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>

另一种方式是:

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

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

  3. 然后比较答案画布和问题画布。

  4. 在画布上绘制比较答案以进行展示。

我将在这里演示如何实现它。您可以裁剪绘图区域以提高性能。

很难确定路径是否完整。但您仍然可以:

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

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

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

如果图像很大,可能会很慢,所以我宁愿只在用户mouseup 或单击检查按钮时检查它。 我也尝试过使用.toDataURL通过字符串比较它们的值,但是它太严格了,不能让你有一个阈值。

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();
<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>

【讨论】:

  • 确实不错,但我如何检测用户何时完成抽奖?路径完成后发出警报或其他内容
  • 完整是指后台路径全部填满?
  • @Hiero 好问题,我需要一些时间来解决它,但我还是想出了一个解决方案,请检查我编辑的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
  • 2014-10-16
  • 1970-01-01
  • 1970-01-01
  • 2013-05-18
  • 1970-01-01
相关资源
最近更新 更多