HTML5 Canvas:对具有更多路径

时间:2016-01-21 12:50:05

标签: javascript canvas html5-canvas

我画的是一个复杂的形状,由6条细线和2条粗线组成。

在代码中我打开8个pathes来执行此操作:

        context.save();
    context.lineWidth=2;
    var TAB_ABSTAND=10;
    var TAB_SAITENZAHL=6;
    var TAB_SEITENDICKE=10;
    for(var i=0;i<TAB_SAITENZAHL;i++)
    {
         context.beginPath();
         context.moveTo(this.clickedX, this.clickedY+(i*TAB_ABSTAND));
         context.lineTo(this.clickedX+this.width, this.clickedY+(i*TAB_ABSTAND));
         context.stroke();  
    }



     context.lineWidth=TAB_SEITENDICKE;

     context.beginPath();
     context.moveTo(this.clickedX, this.clickedY-1);
     context.lineTo(this.clickedX, this.clickedY+TAB_ABSTAND*(TAB_SAITENZAHL-1)+1);
     context.stroke();

     context.beginPath();
     context.moveTo(this.clickedX+this.width, this.clickedY-1);
     context.lineTo(this.clickedX+this.width, this.clickedY+TAB_ABSTAND*(TAB_SAITENZAHL-1)+1);
     context.stroke();    

     context.restore();     

在canvas onmousedown事件中,我想识别是否单击了形状(或数组中的其他形状之一)以实现拖动。

有没有办法使用isPointInPath(x,y)方法来识别是否点击了“形状”中的一条线?

我想要做的是实现一个维护可拖动对象列表的机制。

我发现了什么:

1。)beginPath是唯一一种以某种方式中断路径的上下文方法,即isPointInPath方法无法识别前一个路径

2。)在具有大笔划的单行(即context.lineWidth = 10)上,当isPointInPath方法只是没有曲线的单行时,它不返回true

3.)closePath将最后一行的端点绘制到第一行的起始点,但它不会中断该路径,因此稍后的stroke()始终对lineTo和moveTo-methods生效。 closePath

4。)如果没有在没有抚摸其余部分的情况下调用beginPath(),似乎无法绘制更大的行

5。)moveTo(x,y)实际上跳转到另一个位置,但另一个位置可以是一个为isPointInPath方法返回true的路径,当它不仅包含一行时(见1.))。 / p>

6。)为了可视化pathes,fill()方法很有用

当我想要识别“直线”(绘制为矩形)是否在路径中时,我应该总是使用矩形绘制线条吗?

2 个答案:

答案 0 :(得分:1)

如何测试由多条路径构成的形状

此形状由2条路径组成: Path1 = 4条细红线,Path2 = 2条粗蓝线。

enter image description here

首先,这里是有关道路的信息,可以解释您的观察结果发生的原因

  

beginPath是唯一中断a中路径的上下文方法   方式,isPointInPath无法识别前一个路径   方法

您可以定义多个路径,但isPointInPath只会测试最后定义的路径

路径由路径命令集组成,如下所示:

  • 路径以beginPath命令
  • 开头
  • 然后路径使用moveTolineTo等命令定义其形状。
  • 路径以下一个beginPath命令结束。

所以在你的问题代码中,只有最后一次moveTo + lineTo才会被测试。

 context.beginPath();
 context.moveTo(this.clickedX+this.width, this.clickedY-1);
 context.lineTo(this.clickedX+this.width, this.clickedY+TAB_ABSTAND*(TAB_SAITENZAHL-1)+1);
  

在具有大笔划的单行(即context.lineWidth = 10)上,当isPointInPath方法只是单个时,它不会返回true   没有曲线的线。

数学上,一条线不占空间。因此,您无法测试任何点是否在“一行”中。

在大多数浏览器中(特别是NOT IE / Edge),您可以使用新的isPointInStroke方法来测试一个点是否在一行内。对于跨浏览器兼容性,您必须将一条线“加肥”到路径中,以便您可以在该扩展路径中测试一个点。

  

closePath将最后一行的端点绘制到起始点   第一行,但它不是中断路径,所以以后   stroke()始终对lineTo和moveTo-methods生效   closePath

恕我直言,closePath命名不佳。它不会关闭(不完成)路径。它不是开始路径的“开口支撑”的“闭合支撑”。相反,它只是直接从路径中的当前点绘制一条线,返回到路径中的第一个点。

  

似乎不可能在没有打电话的情况下画一条更大的线   beginPath()没有抚摸其余的部分

您只允许将一种样式应用于一个路径。因此,如果在一组路径命令中定义多个lineWidth,则最后的lineWidth将获胜,整个路径将使用该最后一个线宽进行描边。

  

moveTo(x,y)真的跳到另一个位置,但另一个位置   可以是一个为isPointInPath方法返回true的路径   不仅包括一行(见1.))。

moveTo相当于“拿起你的笔”并将其移动到纸张上的新位置,而不会结束当前的路径命令集

例如:假设您使用beginPath开始路径,然后使用moveTo绘制3个分隔的三角形以将它们分开。然后所有3个三角形都将包含在路径中,所有3个三角形将使用isPointInPath进行测试。

  

要显示pathes,fill()方法很有用

.fill.stroke将命令上下文以可视方式将当前路径绘制到画布上。他们没有完成路径 - 只有下一个beginPath才能完成当前路径。因此,如果你定义一个三角形的两边,stroke(),再次定义三角形的第三边和stroke(),那么前两个边将被描边两次,第三边将被描边一次。

重要信息:您可以定义路径并使用isPointInPath对其进行测试,而无需实际抚摸(或填充)测试路径。这意味着您可以单独重新定义每个路径+ isPointInPath ,而无需在画布上重绘它们。您还可以定义由多个路径组成的单个形状,并使用isPointInPath对多路径形状进行命中测试。

因此,要测试mousedown事件中的哪些形状(形状==路径),您可以重新定义每个多路径并使用context.isPointInPath(mouseX,mouseY)对其进行测试。

要实际绘制形状,您需要多种样式(细线和粗线),因此您必须单独绘制路径,因为每条路径只能获得1个样式。

以下是示例代码和演示:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }

var isDown=false;
var startX,startY;

var shapes=[];
//
var path1=[
  {x:150,y:100},
  {x:50,y:100},
  {x:25,y:75},
  {x:50,y:50},
  {x:150,y:50}
];
path1.linewidth=1;
path1.strokestyle='red';
//
var path2=[
  {x:150,y:50},
  {x:225,y:75},
  {x:150,y:100}
];
path2.linewidth=5;
path2.strokestyle='blue';

var shape1=[path1,path2];
shape1.fill='green';
//
shapes.push(shape1);

// draw both parts of the path onto the canvas
draw(path1);
draw(path2);

$("#canvas").mousedown(function(e){handleMouseDown(e);});

function define(shape){
  ctx.beginPath();
  for(j=0;j<shape.length;j++){
    var p=shape[j];
    ctx.moveTo(p[0].x,p[0].y);
    for(var i=1;i<p.length;i++){
      ctx.lineTo(p[i].x,p[i].y);
    }
  }
}
//
function draw(path){
  ctx.beginPath();
  ctx.moveTo(path[0].x,path[0].y);
  for(var i=1;i<path.length;i++){
    ctx.lineTo(path[i].x,path[i].y);
  }
  ctx.lineWidth=path.linewidth;
  ctx.strokeStyle=path.strokestyle;
  ctx.stroke();
}
//
function dot(x,y,fill){
  ctx.beginPath();
  ctx.arc(x,y,2,0,Math.PI*2);
  ctx.closePath();
  ctx.fillStyle=fill;
  ctx.fill();
}

function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  // get mouseX,mouseY
  var mx=parseInt(e.clientX-offsetX);
  var my=parseInt(e.clientY-offsetY);
  //
  var dotcolor='red';
  for(var i=0;i<shapes.length;i++){
    define(shapes[i]);
    if(ctx.isPointInPath(mx,my)){dotcolor=shapes[i].fill;}
  }
  dot(mx,my,dotcolor);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Click inside and outside the multi-path shape<br>Inside clicks become green dots.</h4>
<canvas id="canvas" width=300 height=300></canvas>

答案 1 :(得分:0)

以下代码正在实现我的期望。我只使用fill()而且从不使用beginPath()

    context.save();
context.lineWidth=1;
var TAB_ABSTAND=10;
var TAB_SAITENZAHL=6;
var TAB_SEITENDICKE=10;

for(var i=0;i<TAB_SAITENZAHL;i++)
{
    context.moveTo(this.clickedX+5, this.clickedY+(i*TAB_ABSTAND));
    context.lineTo(this.clickedX+this.width, this.clickedY+(i*TAB_ABSTAND));
    context.lineTo(this.clickedX+this.width, this.clickedY+(i*TAB_ABSTAND)+1);
    context.lineTo(this.clickedX+5, this.clickedY+(i*TAB_ABSTAND)+1);   
}

context.moveTo(this.clickedX, this.clickedY);
context.lineTo(this.clickedX, this.clickedY+TAB_ABSTAND*(TAB_SAITENZAHL-1)+1);
context.lineTo(this.clickedX+5, this.clickedY+TAB_ABSTAND*(TAB_SAITENZAHL-1)+1);
context.lineTo(this.clickedX+5, this.clickedY);

context.moveTo(this.clickedX+this.width, this.clickedY);
context.lineTo(this.clickedX+this.width, this.clickedY+TAB_ABSTAND*(TAB_SAITENZAHL-1)+1);
context.lineTo(this.clickedX+this.width-5, this.clickedY+TAB_ABSTAND*(TAB_SAITENZAHL-1)+1);
context.lineTo(this.clickedX+this.width-5, this.clickedY);
context.fill();    

context.restore();