如何在HTML画布中制作平行线

6
我有以下条件:
  1. 创建点A、B和C。
  2. 从点A到点B会创建一条线。
  3. 根据点A、B和C的位置创建平行线(参见下图)。
  4. 如果您移动点A,则这些线也会移动,但点B和C仍保持在它们各自的位置上。
  5. 它们可以移动到任何位置。
我想要创建这个:

enter image description here


你能提供任何JSFiddle / 代码片段来开始帮助您吗? - Anonymous0day
3个回答

6

请看下面的图1(我相信您已经了解这个基本的二维几何,但如果没有这个,我的答案将不完整):

Figure 1

已知点A和B的坐标,我们希望找到一个函数,可以在已知x坐标时用来计算y坐标,使得点(x,y)位于直线上。从图1中可以看出:
k = tan(alpha) = (y2 - y1) / (x2 - x1) - 直线的斜率
将点A或B的坐标放入众所周知的直线方程y = kx + m中,我们可以计算m以使方程完整。有了这个方程,对于任何坐标x,我们都可以使用该方程来计算坐标y。好处是它不依赖于点A和B的位置或者直线的斜率(角度) - 您将需要处理垂直/水平线的特殊情况,根据此方程式,y/x将会无限大。
回到您的问题。请看下面的图2:

Figure 2

我们这里有一个非常类似的情况,有一条连接点A和点C的线段,以及一条连接点B和点D的线段。我假设点A在坐标系的中心!虽然通常情况下这并不成立,但这并不是限制,因为您可以进行平移操作将点A放在中心,然后进行计算,最后再平移回来。
使用开头描述的技术,您可以找到连接A和C点以及连接B和D点的直线方程(可以轻松计算出D的坐标)。让我们假设您已经完成了这个步骤:
A-C: y = k1*x(由于线穿过中心A,m为零)
B-D: y = k2*x + m2(由于线不穿过中心A,m2不为零)
最后,您可以使用以下算法绘制这些平行线:
  1. 选择一个空间,使得你想要在x1和x3之间取x坐标。例如,如果你想要4条线,这个空间将是 s = (x3 - x1) / 4等。
  2. 设置值x_start = x1 + s(然后x_start += s),并使用A-C线的方程计算y坐标y_end = k1*x_start。这将给出一个位于A-C线上的点,这是您的线的起点。
  3. 类似地,计算将位于连接B和D的线上的终点:

x_end = x2 + s(稍后x_end += s)

y_end = k2*x_end + m2

  1. 使用这些方程为您想要绘制的所有线计算点(x_start,y_start)和(x_end,y_end)(有|x3 - x1| / desired_num_of_lines 条线)。
每当A点移出当前的A-C线时,您需要形成新的方程式,因为每次这种情况发生时,A-C(和B-D)线的斜率会改变,从而使当前的方程无效。我不会写任何JS代码,但拥有可能解决方案背后的逻辑应该为您自己的实现提供足够的信息。

1
优秀的回应。教一个人如何钓鱼。 - JDB

3

在使用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>


1
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();

现在假设这两条线的长度相同。如果它们不同,方程会稍微复杂一些。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接