如何在 html canvas 中制作平行线
How to make a parallel lines in html canvas
我有这些条件:
- Point A, B and C are created.
- Point A to Point B will create a line.
- Parallel lines are created depending on the position of Point A, B and C (refer to the figure below).
- If you move Point A, the lines will also move but Points B and C remain on their respective positions.
- They can be moved at any position.
我想要的是创建这个:
考虑下面的图 1(我相信您已经知道这个基本的 2D 几何形状,但如果没有这个,我的回答将是不完整的):
A 点和 B 点的坐标已知,我们希望找到可以在 x 坐标已知时用于计算 y 坐标的函数,这样点 (x,y) 位于直线上.从图1:
k = tan(alpha) = (y2 - y1) / (x2 - x1) - the slope of line
将A或B的坐标代入众所周知的直线方程y = kx + m
我们可以计算m
使方程完整。有了这个等式,对于任何坐标 x 我们都可以使用这个等式计算坐标 y。它的好处是它不依赖于点 A 和 B 的位置或线的斜率(角度)- 你必须处理 的特殊情况vertical/horizontal 行,其中 y/x 根据此等式将是无限的。
回到你的问题。看看下面的图2:
我们这里的情况非常相似,A点和C点之间有一条线,B点和D点之间有一条线。我假设点A在坐标系!这通常不会是这种情况,但这实际上不是限制,因为您可以执行 translation 将 A 放在中心,然后进行计算,然后将所有内容翻译回去。
利用开头所述的技巧,你可以求出连接A点和C点的直线和连接B点和D点的直线的直线方程(D坐标可以很容易地计算出来)。假设您就是这么做的:
A-C: y = k1*x (m is zero as line goes through the center A)
B-D: y = k2*x + m2 (m2 is not zero as line doesn't go through the center A)
最后,您可以使用 算法 来绘制这些平行线:
- 选择一个space,你想用它来获取 x1 和 x3 之间的 x 坐标。例如,如果你想要 4 行,这个 space 将是
s = (x3 - x1) / 4
等等。
- 设置值
x_start = x1 + s (and later x_start += s)
,并使用 A-C 线的等式计算 y 坐标 y_end = k1*x_start
。这将为您提供位于 A-C 线上的点,这是您的线的起点。
- 同理计算B、D连线上的终点:
x_end = x2 + s (later x_end += s)
y_end = k2*x_end + m2
- 使用这些等式计算点 (x_start,y_start) 和 (x_end,y_end) 对于所有要绘制的线(有 [=其中 15=] 个)。
每次 A 点移出当前 A-C 线时,您都必须构建新方程,因为每次发生这种情况时,A-C(和 B-D)线的斜率都会发生变化,从而使当前方程无效。
我不打算编写任何 JS 代码,但是了解可能的解决方案背后的逻辑应该会为您提供更多足够的信息来推进您自己的实施。
Džanan 说得对,简单来说,您需要两条线的起点之间的 X 和 Y 偏移量,即点 A 和点 C。绘制从 C 开始的线时,并假设它在 D 处结束,您将需要添加相同的 X 和 Y 偏移量,例如,如果您使用起始坐标 (100, 150) 绘制 AB,如下所示:
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
如果 C 必须从 (150, 200) 开始,这里的偏移量将是
X: 50, Y:50
所以CD会画成
context.beginPath();
context.moveTo(150, 200);
context.lineTo((450+50), (50+50));
context.stroke();
现在假设两条线的长度相同。如果它们不同,方程式会稍微复杂一些。
始终认为,在使用 Context2D
时,使用转换(translate
、rotate
、scale
)可以节省一些数学运算。
通过这些变换,您可以像使用钢笔一样思考您的绘图:您将钢笔放在哪里?你下一步要去哪里 (translate
)?你 rotate
页面吗?你离页面更近还是更远 (scale
) ?
此处您想从 A 开始,然后沿着 AC 移动。
在此过程中的每一步,您都希望绘制 AB 矢量。
如您所见,这里只是简单的向量数学运算,所以如果您还记得 AB 向量有 (B.x-A.x, B.y-A.y) 坐标,你知道你需要的大部分数学知识。
// boilerPlate
var ctx = document.getElementById('cv').getContext('2d');
ctx.strokeStyle = '#000';
// params : Points : {x,y}
var A, B, C;
A = { x: 20, y: 170 };
B = { x: 80, y: 60 };
C = { x: 140, y: 120 };
// param : number of lines to draw.
var stepCount = 5;
// ----------
// compute AB vector = B - A
var AB = { x: B.x - A.x, y: B.y - A.y };
// compute step : ( C - A ) / stepCount
var step = { x: (C.x - A.x) / stepCount, y: (C.y - A.y) / stepCount };
// -- start draw
ctx.save();
// Move pen to A
ctx.translate(A.x, A.y);
for (var i = 0; i <= stepCount; i++) {
// draw AB vector at current position
ctx.lineWidth= ( i==0 || i==stepCount ) ? 2 : 1 ;
drawVector(AB);
// move pen one step further
ctx.translate(step.x, step.y);
}
ctx.restore();
// --
// draws vector V at the current origin ((0,0)) of the context.
function drawVector(V) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(V.x, V.y);
ctx.stroke();
}
// ----------
// legend
drawPoint(A, 'A');
drawPoint(B, 'B');
drawPoint(C, 'C');
function drawPoint(P, name) {
ctx.beginPath();
ctx.arc(P.x, P.y, 3, 0, 6.28);
ctx.fill();
ctx.strokeText(name, P.x + 6, P.y + 6);
}
<canvas id='cv' width=300 height=200></canvas>
我有这些条件:
- Point A, B and C are created.
- Point A to Point B will create a line.
- Parallel lines are created depending on the position of Point A, B and C (refer to the figure below).
- If you move Point A, the lines will also move but Points B and C remain on their respective positions.
- They can be moved at any position.
我想要的是创建这个:
考虑下面的图 1(我相信您已经知道这个基本的 2D 几何形状,但如果没有这个,我的回答将是不完整的):
A 点和 B 点的坐标已知,我们希望找到可以在 x 坐标已知时用于计算 y 坐标的函数,这样点 (x,y) 位于直线上.从图1:
k = tan(alpha) = (y2 - y1) / (x2 - x1) - the slope of line
将A或B的坐标代入众所周知的直线方程y = kx + m
我们可以计算m
使方程完整。有了这个等式,对于任何坐标 x 我们都可以使用这个等式计算坐标 y。它的好处是它不依赖于点 A 和 B 的位置或线的斜率(角度)- 你必须处理 的特殊情况vertical/horizontal 行,其中 y/x 根据此等式将是无限的。
回到你的问题。看看下面的图2:
我们这里的情况非常相似,A点和C点之间有一条线,B点和D点之间有一条线。我假设点A在坐标系!这通常不会是这种情况,但这实际上不是限制,因为您可以执行 translation 将 A 放在中心,然后进行计算,然后将所有内容翻译回去。
利用开头所述的技巧,你可以求出连接A点和C点的直线和连接B点和D点的直线的直线方程(D坐标可以很容易地计算出来)。假设您就是这么做的:
A-C: y = k1*x (m is zero as line goes through the center A)
B-D: y = k2*x + m2 (m2 is not zero as line doesn't go through the center A)
最后,您可以使用 算法 来绘制这些平行线:
- 选择一个space,你想用它来获取 x1 和 x3 之间的 x 坐标。例如,如果你想要 4 行,这个 space 将是
s = (x3 - x1) / 4
等等。 - 设置值
x_start = x1 + s (and later x_start += s)
,并使用 A-C 线的等式计算 y 坐标y_end = k1*x_start
。这将为您提供位于 A-C 线上的点,这是您的线的起点。 - 同理计算B、D连线上的终点:
x_end = x2 + s (later x_end += s)
y_end = k2*x_end + m2
- 使用这些等式计算点 (x_start,y_start) 和 (x_end,y_end) 对于所有要绘制的线(有 [=其中 15=] 个)。
每次 A 点移出当前 A-C 线时,您都必须构建新方程,因为每次发生这种情况时,A-C(和 B-D)线的斜率都会发生变化,从而使当前方程无效。
我不打算编写任何 JS 代码,但是了解可能的解决方案背后的逻辑应该会为您提供更多足够的信息来推进您自己的实施。
Džanan 说得对,简单来说,您需要两条线的起点之间的 X 和 Y 偏移量,即点 A 和点 C。绘制从 C 开始的线时,并假设它在 D 处结束,您将需要添加相同的 X 和 Y 偏移量,例如,如果您使用起始坐标 (100, 150) 绘制 AB,如下所示:
context.beginPath();
context.moveTo(100, 150);
context.lineTo(450, 50);
context.stroke();
如果 C 必须从 (150, 200) 开始,这里的偏移量将是 X: 50, Y:50
所以CD会画成
context.beginPath();
context.moveTo(150, 200);
context.lineTo((450+50), (50+50));
context.stroke();
现在假设两条线的长度相同。如果它们不同,方程式会稍微复杂一些。
始终认为,在使用 Context2D
时,使用转换(translate
、rotate
、scale
)可以节省一些数学运算。
通过这些变换,您可以像使用钢笔一样思考您的绘图:您将钢笔放在哪里?你下一步要去哪里 (translate
)?你 rotate
页面吗?你离页面更近还是更远 (scale
) ?
此处您想从 A 开始,然后沿着 AC 移动。
在此过程中的每一步,您都希望绘制 AB 矢量。
如您所见,这里只是简单的向量数学运算,所以如果您还记得 AB 向量有 (B.x-A.x, B.y-A.y) 坐标,你知道你需要的大部分数学知识。
// boilerPlate
var ctx = document.getElementById('cv').getContext('2d');
ctx.strokeStyle = '#000';
// params : Points : {x,y}
var A, B, C;
A = { x: 20, y: 170 };
B = { x: 80, y: 60 };
C = { x: 140, y: 120 };
// param : number of lines to draw.
var stepCount = 5;
// ----------
// compute AB vector = B - A
var AB = { x: B.x - A.x, y: B.y - A.y };
// compute step : ( C - A ) / stepCount
var step = { x: (C.x - A.x) / stepCount, y: (C.y - A.y) / stepCount };
// -- start draw
ctx.save();
// Move pen to A
ctx.translate(A.x, A.y);
for (var i = 0; i <= stepCount; i++) {
// draw AB vector at current position
ctx.lineWidth= ( i==0 || i==stepCount ) ? 2 : 1 ;
drawVector(AB);
// move pen one step further
ctx.translate(step.x, step.y);
}
ctx.restore();
// --
// draws vector V at the current origin ((0,0)) of the context.
function drawVector(V) {
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(V.x, V.y);
ctx.stroke();
}
// ----------
// legend
drawPoint(A, 'A');
drawPoint(B, 'B');
drawPoint(C, 'C');
function drawPoint(P, name) {
ctx.beginPath();
ctx.arc(P.x, P.y, 3, 0, 6.28);
ctx.fill();
ctx.strokeText(name, P.x + 6, P.y + 6);
}
<canvas id='cv' width=300 height=200></canvas>