如何在 canvas 的顶点之间绘制平行边(箭头)?
How to draw parallel edges (arrows) between vertices with canvas?
我正在使用 Javascript 进行 flow-network 可视化。
顶点表示为圆,边表示为箭头。
这是我的优势class:
function Edge(u, v) {
this.u = u; // start vertex
this.v = v; // end vertex
this.draw = function() {
var x1 = u.x;
var y1 = u.y;
var x2 = v.x;
var y2 = v.y;
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
var dx = x1 - x2;
var dy = y1 - y2;
var length = Math.sqrt(dx * dx + dy * dy);
x1 = x1 - Math.round(dx / ((length / (radius))));
y1 = y1 - Math.round(dy / ((length / (radius))));
x2 = x2 + Math.round(dx / ((length / (radius))));
y2 = y2 + Math.round(dy / ((length / (radius))));
// calculate the angle of the edge
var deg = (Math.atan(dy / dx)) * 180.0 / Math.PI;
if (dx < 0) {
deg += 180.0;
}
if (deg < 0) {
deg += 360.0;
}
// calculate the angle for the two triangle points
var deg1 = ((deg + 25 + 90) % 360) * Math.PI * 2 / 360.0;
var deg2 = ((deg + 335 + 90) % 360) * Math.PI * 2 / 360.0;
// calculate the triangle points
var arrowx = [];
var arrowy = [];
arrowx[0] = x2;
arrowy[0] = y2;
arrowx[1] = Math.round(x2 + 12 * Math.sin(deg1));
arrowy[1] = Math.round(y2 - 12 * Math.cos(deg1));
arrowx[2] = Math.round(x2 + 12 * Math.sin(deg2));
arrowy[2] = Math.round(y2 - 12 * Math.cos(deg2));
context.beginPath();
context.moveTo(arrowx[0], arrowy[0]);
context.lineTo(arrowx[1], arrowy[1]);
context.lineTo(arrowx[2], arrowy[2]);
context.closePath();
context.stroke();
context.fillStyle = "black";
context.fill();
};
}
给定代码
var canvas = document.getElementById('canvas'); // canvas element
var context = canvas.getContext("2d");
context.lineWidth = 1;
context.strokeStyle = "black";
var radius = 20; // vertex radius
var u = {
x: 50,
y: 80
};
var v = {
x: 150,
y: 200
};
var e = new Edge(u, v);
e.draw();
draw() 函数将在两个顶点之间绘制一条边,如下所示:
如果我们添加代码
var k = new Edge(v, u);
k.draw();
我们将得到:
但我想在两个方向上绘制边,如下所示:
(抱歉我的画技不好)
当然顶点和边的方向是不固定的。
JSFiddle 上的一个工作示例(具有绘图顶点功能):
https://jsfiddle.net/Romansko/0fu01oec/18/
将轴与一条线对齐。
如果旋转渲染图使其与直线对齐,可以使一切变得更容易一些。一旦你这样做了,就很容易在线的上方或下方绘制,因为它只是在 y 方向上,而沿着线是 x 方向。
因此如果你有一行
const line = {
p1 : { x : ? , y : ? },
p2 : { x : ? , y : ? },
};
将其转换为向量并归一化该向量
// as vector from p1 to p2
var nx = line.p2.x - line.p1.x;
var ny = line.p2.y - line.p1.y;
// then get length
const len = Math.sqrt(nx * nx + ny * ny);
// use the length to normalise the vector
nx /= len;
ny /= len;
归一化向量表示我们要沿其渲染的新 x 轴,y 轴与该轴成 90 度角。我们可以使用 setTransform 将轴和原点 (0,0) 设置在直线的起点。
ctx.setTransform(
nx, ny, // the x axis
-ny, nx, // the y axis at 90 deg to the x axis
line.p1.x, line.p1.y // the origin (0,0)
)
现在渲染线条和箭头很容易,因为它们是轴对齐的
ctx.beginPath();
ctx.lineTo(0,0); // start of line
ctx.lineTo(len,0); // end of line
ctx.stroke();
// add the arrow head
ctx.beginPath();
ctx.lineTo(len,0); // tip of arrow
ctx.lineTo(len - 10, 10);
ctx.lineTo(len - 10, -10);
ctx.fill();
渲染偏离中心的两条线
var offset = 10;
ctx.beginPath();
ctx.lineTo(0,offset); // start of line
ctx.lineTo(len,offset); // end of line
ctx.moveTo(0,-offset); // start of second line
ctx.lineTo(len,-offset); // end of second line
ctx.stroke();
// add the arrow head
ctx.beginPath();
ctx.lineTo(len,offset); // tip of arrow
ctx.lineTo(len - 10, offset+10);
ctx.lineTo(len - 10, offset-10);
ctx.fill();
offset = -10;
// add second arrow head
ctx.beginPath();
ctx.lineTo(0,offset); // tip of arrow
ctx.lineTo(10, offset+10);
ctx.lineTo(10, offset-10);
ctx.fill();
您可以使用
重置转换
ctx.setTransform(1,0,0,1,0,0); // restore default transform
我正在使用 Javascript 进行 flow-network 可视化。 顶点表示为圆,边表示为箭头。
这是我的优势class:
function Edge(u, v) {
this.u = u; // start vertex
this.v = v; // end vertex
this.draw = function() {
var x1 = u.x;
var y1 = u.y;
var x2 = v.x;
var y2 = v.y;
context.beginPath();
context.moveTo(x1, y1);
context.lineTo(x2, y2);
context.stroke();
var dx = x1 - x2;
var dy = y1 - y2;
var length = Math.sqrt(dx * dx + dy * dy);
x1 = x1 - Math.round(dx / ((length / (radius))));
y1 = y1 - Math.round(dy / ((length / (radius))));
x2 = x2 + Math.round(dx / ((length / (radius))));
y2 = y2 + Math.round(dy / ((length / (radius))));
// calculate the angle of the edge
var deg = (Math.atan(dy / dx)) * 180.0 / Math.PI;
if (dx < 0) {
deg += 180.0;
}
if (deg < 0) {
deg += 360.0;
}
// calculate the angle for the two triangle points
var deg1 = ((deg + 25 + 90) % 360) * Math.PI * 2 / 360.0;
var deg2 = ((deg + 335 + 90) % 360) * Math.PI * 2 / 360.0;
// calculate the triangle points
var arrowx = [];
var arrowy = [];
arrowx[0] = x2;
arrowy[0] = y2;
arrowx[1] = Math.round(x2 + 12 * Math.sin(deg1));
arrowy[1] = Math.round(y2 - 12 * Math.cos(deg1));
arrowx[2] = Math.round(x2 + 12 * Math.sin(deg2));
arrowy[2] = Math.round(y2 - 12 * Math.cos(deg2));
context.beginPath();
context.moveTo(arrowx[0], arrowy[0]);
context.lineTo(arrowx[1], arrowy[1]);
context.lineTo(arrowx[2], arrowy[2]);
context.closePath();
context.stroke();
context.fillStyle = "black";
context.fill();
};
}
给定代码
var canvas = document.getElementById('canvas'); // canvas element
var context = canvas.getContext("2d");
context.lineWidth = 1;
context.strokeStyle = "black";
var radius = 20; // vertex radius
var u = {
x: 50,
y: 80
};
var v = {
x: 150,
y: 200
};
var e = new Edge(u, v);
e.draw();
draw() 函数将在两个顶点之间绘制一条边,如下所示:
如果我们添加代码
var k = new Edge(v, u);
k.draw();
我们将得到:
但我想在两个方向上绘制边,如下所示: (抱歉我的画技不好)
当然顶点和边的方向是不固定的。 JSFiddle 上的一个工作示例(具有绘图顶点功能): https://jsfiddle.net/Romansko/0fu01oec/18/
将轴与一条线对齐。
如果旋转渲染图使其与直线对齐,可以使一切变得更容易一些。一旦你这样做了,就很容易在线的上方或下方绘制,因为它只是在 y 方向上,而沿着线是 x 方向。
因此如果你有一行
const line = {
p1 : { x : ? , y : ? },
p2 : { x : ? , y : ? },
};
将其转换为向量并归一化该向量
// as vector from p1 to p2
var nx = line.p2.x - line.p1.x;
var ny = line.p2.y - line.p1.y;
// then get length
const len = Math.sqrt(nx * nx + ny * ny);
// use the length to normalise the vector
nx /= len;
ny /= len;
归一化向量表示我们要沿其渲染的新 x 轴,y 轴与该轴成 90 度角。我们可以使用 setTransform 将轴和原点 (0,0) 设置在直线的起点。
ctx.setTransform(
nx, ny, // the x axis
-ny, nx, // the y axis at 90 deg to the x axis
line.p1.x, line.p1.y // the origin (0,0)
)
现在渲染线条和箭头很容易,因为它们是轴对齐的
ctx.beginPath();
ctx.lineTo(0,0); // start of line
ctx.lineTo(len,0); // end of line
ctx.stroke();
// add the arrow head
ctx.beginPath();
ctx.lineTo(len,0); // tip of arrow
ctx.lineTo(len - 10, 10);
ctx.lineTo(len - 10, -10);
ctx.fill();
渲染偏离中心的两条线
var offset = 10;
ctx.beginPath();
ctx.lineTo(0,offset); // start of line
ctx.lineTo(len,offset); // end of line
ctx.moveTo(0,-offset); // start of second line
ctx.lineTo(len,-offset); // end of second line
ctx.stroke();
// add the arrow head
ctx.beginPath();
ctx.lineTo(len,offset); // tip of arrow
ctx.lineTo(len - 10, offset+10);
ctx.lineTo(len - 10, offset-10);
ctx.fill();
offset = -10;
// add second arrow head
ctx.beginPath();
ctx.lineTo(0,offset); // tip of arrow
ctx.lineTo(10, offset+10);
ctx.lineTo(10, offset-10);
ctx.fill();
您可以使用
重置转换ctx.setTransform(1,0,0,1,0,0); // restore default transform