如何在<canvas>?</canvas>上约束绘图

时间:2014-09-18 12:00:14

标签: javascript html5 canvas touch

我想创建一个移动网页,其中一个形状出现在屏幕上,用户只能用他/她的手指在形状轮廓上追踪,然后会出现一个新的形状。这个library有一些我想要做的好例子,只是有更多的形状。我已经在触摸设备herehere上的画布上找到了几个很好的例子。我不知道的是如何限制线条,所以你只用一条连续线画在路径上。是否有内置的内容可以指定您可以绘制的唯一路径,还是我必须手动编写该逻辑?

1 个答案:

答案 0 :(得分:1)

我们可以将问题分成两部分:
1)了解用户是否在路径上 2)知道用户是否进入了所有路径部分。

对于1),我们可以使用isPointInPath context2D方法来了解鼠标/触摸点(x,y)是否在曲线上。这里的约束是你必须建立一个封闭的表面,这意味着一个由fill()绘制的表面,而不是一个用stroke()构建的表面。因此,如果你正在抚摸粗线,你必须做一些小数学来构建moveTo + lineTo + fill之外的相应数字。

For 2):为你的形状建立一个'检查点'列表。例如,您可能有一个圆圈的8个控制点。然后决定用户“激活”检查点的距离。现在算法是伪代码:

 mouseDown => check()
 mouseMove => if mouse is down, check()

 checkPointList = [  [ 10, 40, false ] , [ centerX, centerY, isChecked], ... ] ;
 checked = 0;

 function check() {
    clear screen
    draw the path
    if (mouse down and mouse point on path) {
       for ( checkPoint in CheckPointList) {
           if (checkPoint near enough of mouse) {
              checkPoint[3]=true;
              checked++;
           }
       }
    if (checked == checkPointList.length) ==>>> User DID draw all the shape.
    }  else
    clear the flags of the checkPointList;      
    checked=0;
 }

我在这里做了一个简单的简单演示,这可以说是工作。 控制点在停用时显示为红色,激活时显示为绿色:

http://jsbin.com/wekaxiguwiyo/1/edit?js,output

// boilerplate
var cv = document.getElementById('cv');
var ctx = cv.getContext('2d');

function draw() {
  ctx.clearRect(0,0,300,300);
  drawShape();  
  drawCP();
}

// Shape
function drawShape() {
  ctx.beginPath();
  ctx.moveTo(30,5);
  ctx.lineTo(80,5);
  ctx.lineTo(80, 300);
  ctx.lineTo(30,300);
  ctx.closePath();
  ctx.lineWidth= 16;
  ctx.fillStyle='#000';
  ctx.fill();
}

// Control points
var points = [ [50, 50, false], [50,120, false], [50, 190, false],[50,260, false ]  ];
var pointsCount = 0;

function drawCP() {
  for (var i=0; i<points.length; i++) {
    var p = points[i];
    ctx.fillStyle=p[2]?'#0F0':'#F00';
    ctx.fillRect(p[0]-1, p[1]-1, 2, 2);
  }
}

function resetCP() {
  for (var i=0; i<points.length; i++) {
    points[i][2]=false;
  }
  pointsCount=0;
}

function testCP(x,y) {
  var d=30;
  d=sq(d);
  for (var i=0; i<points.length; i++) {
    if (sq(points[i][0]-x)+sq(points[i][1]-y)<d) { 
      if (!points[i][2]) pointsCount++;
      points[i][2]=true
    };
  }
}

function sq(x) { return x*x; }

//
draw();

// most simple event handling
addEventListener('mousemove', mouseMove);

var r = cv.getBoundingClientRect();

function mouseMove(e) {
  var x = e.pageX-r.left;
  var y = e.pageY-r.top;
  draw();
  ctx.fillStyle='#000';

  if (ctx.isPointInPath(x,y)) { 
      ctx.fillStyle='#F00';
      testCP(x,y);
  } else {
      resetCP();
  }
  ctx.fillRect(x-3,y-3,6,6);
  var pathDrawn = (pointsCount == points.length);
  if (pathDrawn) ctx.fillText('Shape drawn!!', 150, 150);
}