我画的是一个复杂的形状,由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()方法很有用
当我想要识别“直线”(绘制为矩形)是否在路径中时,我应该总是使用矩形绘制线条吗?
答案 0 :(得分:1)
如何测试由多条路径构成的形状
此形状由2条路径组成: Path1 = 4条细红线,Path2 = 2条粗蓝线。
首先,这里是有关道路的信息,可以解释您的观察结果发生的原因
beginPath
是唯一中断a中路径的上下文方法 方式,isPointInPath无法识别前一个路径 方法
您可以定义多个路径,但isPointInPath
只会测试最后定义的路径。
路径由路径命令集组成,如下所示:
beginPath
命令moveTo
,lineTo
等命令定义其形状。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();