在一行上渲染一个箭头
如果你使用的是纯画布,它会不那么繁琐。
首先是两个圆圈
const circle1 = {
x : ?,
y : ?,
r : ?, // Radius
lineWidth : ?,
}
const circle2 = {
x : ?,
y : ?,
r : ?,
lineWidth : ?,
}
然后是箭头说明
const arrow = {
width : ?,
depth : ?, // tip to back of arrow head
}
数学。
获取circle1到circle2的向量
var vx = circle2.x - circle1.x;
var vy = circle2.y - circle1.y;
他们之间的距离
var dist = Math.sqrt(vx * vx + vy * vy);
您现在需要通过将向量除以长度来规范化向量。这使得向量长一个单位
vx /= dist;
vy /= dist;
现在您可以渲染箭头了。尖端是圆2的边缘之一。要找到该点,请从距离中减去半径和线宽的一半
const aDist = dist - (circle2.r + circle2.lineWidth / 2);
归一化向量可以乘以距离来找到位置,或者如果使用 canvas 2D API,您可以使用法线设置变换,然后它全部与 x 轴对齐。
ctx.beginPath(); // drawing the arrow head
ctx.lineTo(circle1.x + vx * aDist, circle1.y + vy * aDist); // tip of arrow
我们需要从尖端向后移动并以 90 度从直线向外移动,您可以通过交换 x、y 部分并否定新的 x 来将矢量旋转 90 度。
ctx.lineTo(
circle1.x + vx * (aDist - arrow.depth) - vy * arrow.width,
circle1.y + vy * (aDist - arrow.depth) + vx * arrow.width
// ^------- along line -----^ ^--Out from line--^
);
// and the other side
ctx.lineTo(
circle1.x + vx * (aDist - arrow.depth) + vy * arrow.width,
circle1.y + vy * (aDist - arrow.depth) - vx * arrow.width
// ^------- along line -----^ ^--Out from line--^
);
// ctx.closePath();
ctx.fill();
或者如果你使用转换
ctx.setTransform(vx,vy,-vy,vx,circle1.x,circle1.y); // set the circle1 as origin
// and use the normal to
// align the x axis
ctx.beginPath(); // drawing the arrow head
ctx.lineTo(aDist,0); // tip of arrow
ctx.lineTo(aDist - arrow.depth, arrow.width);
ctx.lineTo(aDist - arrow.depth, -arrow.width);
// ctx.closePath();
ctx.fill();
ctx.setTransform(1,0,0,1,0,0); // restore default transform.
示例
const ctx = canvas.getContext("2d");
const circle1 = {
x : 50,
y : 100,
r : 45, // Radius
lineWidth : 3,
}
const circle2 = {
x : 250,
y : 50,
r : 45,
lineWidth : 3,
}
const arrow = {
width : 10,
depth : 20, // tip to back of arrow head
}
var vx = circle2.x - circle1.x;
var vy = circle2.y - circle1.y;
var dist = Math.sqrt(vx * vx + vy * vy);
vx /= dist;
vy /= dist;
const aDist = dist - (circle2.r + circle2.lineWidth / 2);
ctx.lineWidth = circle1.lineWidth;
ctx.beginPath();
ctx.arc(circle1.x, circle1.y, circle1.r, 0, 2 * Math.PI);
ctx.moveTo(circle2.x + circle2.r, circle2.y);
ctx.arc(circle2.x, circle2.y, circle2.r, 0, 2 * Math.PI);
ctx.moveTo(circle1.x, circle1.y);
ctx.lineTo(circle2.x, circle2.y);
ctx.stroke();
ctx.beginPath(); // drawing the arrow head
ctx.lineTo(circle1.x + vx * aDist, circle1.y + vy * aDist); // tip of arrow
ctx.lineTo(
circle1.x + vx * (aDist - arrow.depth) - vy * arrow.width,
circle1.y + vy * (aDist - arrow.depth) + vx * arrow.width
// ^------- along line -----^ ^--Out from line--^
);
// and the other side
ctx.lineTo(
circle1.x + vx * (aDist - arrow.depth) + vy * arrow.width,
circle1.y + vy * (aDist - arrow.depth) - vx * arrow.width
// ^------- along line -----^ ^--Out from line--^
);
// ctx.closePath();
ctx.fill();
canvas { border : 2px solid black; }
const ctx = canvas.getContext("2d");
<canvas id="canvas"></canvas>
Example2 使用setTransform
这使用 setTransform 来减少数学量。此示例也使用线宽,但箭头指向圆圈
const ctx = canvas.getContext("2d");
const circle1 = {
x : 50,
y : 100,
r : 45, // Radius
lineWidth : 3,
}
const circle2 = {
x : 250,
y : 50,
r : 45,
lineWidth : 3,
}
const arrow = {
width : 10,
depth : 20, // tip to back of arrow head
}
var vx = circle2.x - circle1.x;
var vy = circle2.y - circle1.y;
var dist = Math.sqrt(vx * vx + vy * vy);
vx /= dist;
vy /= dist;
const aDist = dist - (circle2.r + circle2.lineWidth / 2);
ctx.lineWidth = circle1.lineWidth;
ctx.beginPath();
ctx.arc(circle1.x, circle1.y, circle1.r, 0, 2 * Math.PI);
ctx.moveTo(circle2.x + circle2.r, circle2.y);
ctx.arc(circle2.x, circle2.y, circle2.r, 0, 2 * Math.PI);
ctx.moveTo(circle1.x, circle1.y);
ctx.lineTo(circle2.x, circle2.y);
ctx.stroke();
ctx.beginPath(); // drawing the arrow head
ctx.setTransform(vx, vy, -vy, vx, circle1.x, circle1.y); // set the circle1 as origin
// and use the normal to
// align the x axis
ctx.beginPath(); // drawing the arrow head
ctx.lineTo(aDist,-circle1.lineWidth/2); // tip of arrow circle1.linewidth is same for line between circles
ctx.lineTo(aDist,circle1.lineWidth/2); // tip of arrow circle1.linewidth is same for line between circles
ctx.lineTo(aDist - arrow.depth, arrow.width + circle1.lineWidth/2);
ctx.lineTo(aDist - arrow.depth, -arrow.width - circle1.lineWidth/2);
// ctx.closePath();
ctx.fill();
ctx.setTransform(1,0,0,1,0,0); // restore default transform.
ctx.fill();
canvas { border : 2px solid black; }
const ctx = canvas.getContext("2d");
<canvas id="canvas"></canvas>