7

我需要一种机制来检测具有不同笔划宽度的直线、曲线和多段线的鼠标悬停事件,我已经为矩形和椭圆建立了这样的机制,所以我对画布 API 并不陌生。我会勾勒出所有绘制对象的轮廓并检测鼠标在它们上方的位置,当矩形或椭圆的笔画宽度超过 1 像素时,我会扩展路径以使其也包含边框。例如,对于线条和折线,当我的 lineWidth 为 20 像素时,我很难理解如何扩展它们。

我的问题是:如何在某些形状路径中转换直线、曲线和多段线,以便该路径可以包含它们的所有宽度?

红色路径,黑色描边路径

我需要创建的路径包含此图像中用黑色表示的线/曲线宽度。

----------更多信息----------

我将尝试简化问题:我们有 2 个点(在下图中以红色表示),它们形成一条具有特定公式(y = mx + n)的线,我需要确定垂直线的公式通过这两个初始点之后,需要确定“蓝色”点的位置,它们在context.lineWidth值一半的距离处,当所有点都确定后,可以创建使用 moveTo() 和 lineTo() 序列的新路径。这种方法应该适用于使用控制点的二次曲线和贝塞尔曲线。问题只存在于这些数学计算中。

4

3 回答 3

3

我在这里找到了 math.stackexchange 的解决方案,这个解决方案仅适用于线条,但它适用于经过一些特定修改的曲线和折线。首先,我们需要确定通过两个初始点的线的公式:

第1步

我们的观点:P1(x1, y1) 和 P2(x2, y2)

点与相邻点之间的距离:d

一般形式:Ax + By + C = 0

其中:A = y2 - y1;B = x1 - x2;C = x2y1 - x1y2。

之后,有必要以简写形式定义这个公式:

第2步

简写形式:y = mx +n

其中:m = - A / B;n = - C / B.(当 B != 0 时)

如果 A == 0 那么我们有公式:y = C(当我们有一条水平线时)

如果 B == 0 那么我们有公式:x = C(当我们有一条垂直线时)

当我们有直线的斜率时,我们需要它上面的垂直线的斜率:

第 3 步

垂线斜率:m2 = - 1 / m

如果 A == 0 或 B == 0 转到步骤 4

现在我们需要获取两个初始点的相邻点:

第4步

我会将相邻点记为 P1N1,第一个点为 P1N2,第二个点为 P2N1 和 P2N2

对于特殊情况(水平和垂直线,当 A == 0 或 B == 0 时),我们将有:

对于 A == 0(水平线):

P1N1(x1, y1 - d / 2); P1N2(x1, y1 + d / 2); P2N1(x2, y2 + d / 2); P2N2(x2, y2 - d / 2)。

对于 B == 0(垂直线):

P1N1(x1 - d / 2, y1); P1N2(x1 + d / 2, y1); P2N1(x2 + d / 2, y2); P2N2(x2 - d / 2, y2)。

对于其他情况(A != 0 和 B != 0):

P1N1:

x = (d / 2) / Math.sqrt(1 + Math.pow(m2, 2)) + x1;

y = (m2 * (d / 2)) / Math.sqrt(1 + Math.pow(m2, 2)) + y1;

P1N2:

x = - (d / 2) / Math.sqrt(1 + Math.pow(m2, 2)) + x1;

y = - (m2 * (d / 2)) / Math.sqrt(1 + Math.pow(m2, 2)) + y1;

P2N1:

x = (d / 2) / Math.sqrt(1 + Math.pow(m2, 2)) + x2;

y = (m2 * (d / 2)) / Math.sqrt(1 + Math.pow(m2, 2)) + y2;

P2N2:

x = - (d / 2) / Math.sqrt(1 + Math.pow(m2, 2)) + x2;

y = - (m2 * (d / 2)) / Math.sqrt(1 + Math.pow(m2, 2)) + y2;

如果您想在应用程序中实现这些公式,您应该缓存一些结果以提高性能。

于 2012-07-18T06:26:53.683 回答
3

您是否尝试过使用 Canvas 2D API 的 .isPointInStroke() 方法:

ctx.isPointInStroke(x, y);
ctx.isPointInStroke(path, x, y);

看看: https ://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/isPointInStroke

于 2017-10-03T15:15:27.793 回答
2

如果你的贝塞尔曲线是以下形式。

x(t) = x0 + x1*t + x2*t*t + x3*t*t*t
y(t) = y0 + y1*t + y2*t*t + y3*t*t*t

然后你必须计算它的导数,这将给出任意点的切线。

x'(t) = x1 + 2*x2*t + 3*x3*t*t
y'(t) = y1 + 2*y2*t + 3*y3*t*t

和任何一点的法线。法线是任何一点的垂直线,也是两点的支撑线。

(-y'(t), x'(t))
((y'(t), -(x'(t))
于 2012-07-16T23:19:14.850 回答