geometry - KonvaJS 连接正方形并正确放置线条?
问题描述
所以我正在使用 KonvaJS 和 KonvaReact 构建一个 UML 绘图工具,为此我需要将形状与线条连接起来。我在网站上看到了有关连接对象的教程https://konvajs.org/docs/sandbox/Connected_Objects.html。
他们使用一个函数get_connecter_points
,根据圆上的弧度计算直线的位置。
function getConnectorPoints(from, to) {
const dx = to.x - from.x;
const dy = to.y - from.y;
let angle = Math.atan2(-dy, dx);
const radius = 50;
return [
from.x + -radius * Math.cos(angle + Math.PI),
from.y + radius * Math.sin(angle + Math.PI),
to.x + -radius * Math.cos(angle),
to.y + radius * Math.sin(angle)
];
}
我试图想出一个类似的功能,但无法想出一个好的解决方案或找到一个好的例子。正如您在图像中看到的,我刚刚在函数中返回了 from x 和 y 以及 to x 和 y,因此这些线条将放置在每个正方形的左上角。
该函数的目标应该是将线条放在正方形边的中间和正方形的正确边上。因此,当 to 方块放在下方时,它应该出现在底部。
因此,如果有人有解决方案,我们将不胜感激。
解决方案
对于矩形,数学比圆形要复杂一些。
首先,您需要计算两个对象之间的连接线的角度:
function getCenter(node) {
return {
x: node.x() + node.width() / 2,
y: node.y() + node.height() / 2
}
}
const c1 = getCenter(object1);
const c2 = getCenter(object2;
const dx = c1.x - c2.x;
const dy = c1.y - c2.y;
const angle = Math.atan2(-dy, dx);
其次,当你知道角度时,你需要一个函数,它可以找到矩形边框的一个点,你可以用它来连接另一个对象。
function getRectangleBorderPoint(radians, size, sideOffset = 0) {
const width = size.width + sideOffset * 2;
const height = size.height + sideOffset * 2;
radians %= 2 * Math.PI;
if (radians < 0) {
radians += Math.PI * 2;
}
const phi = Math.atan(height / width);
let x, y;
if (
(radians >= 2 * Math.PI - phi && radians <= 2 * Math.PI) ||
(radians >= 0 && radians <= phi)
) {
x = width / 2;
y = Math.tan(radians) * x;
} else if (radians >= phi && radians <= Math.PI - phi) {
y = height / 2;
x = y / Math.tan(radians);
} else if (radians >= Math.PI - phi && radians <= Math.PI + phi) {
x = -width / 2;
y = Math.tan(radians) * x;
} else if (radians >= Math.PI + phi && radians <= 2 * Math.PI - phi) {
y = -height / 2;
x = y / Math.tan(radians);
}
return {
x: -Math.round(x),
y: Math.round(y)
};
}
现在,您只需要为线条形状生成点:
function getPoints(r1, r2) {
const c1 = getCenter(r1);
const c2 = getCenter(r2);
const dx = c1.x - c2.x;
const dy = c1.y - c2.y;
const angle = Math.atan2(-dy, dx);
const startOffset = getRectangleBorderPoint(angle + Math.PI, r1.size());
const endOffset = getRectangleBorderPoint(angle, r2.size());
const start = {
x: c1.x - startOffset.x,
y: c1.y - startOffset.y
};
const end = {
x: c2.x - endOffset.x,
y: c2.y - endOffset.y
};
return [start.x, start.y, end.x, end.y]
}
function updateLine() {
const points = getPoints(rect1, rect2);
line.points(points);
}
所有这些作为演示:
function getRectangleBorderPoint(radians, size, sideOffset = 0) {
const width = size.width + sideOffset * 2;
const height = size.height + sideOffset * 2;
radians %= 2 * Math.PI;
if (radians < 0) {
radians += Math.PI * 2;
}
const phi = Math.atan(height / width);
let x, y;
if (
(radians >= 2 * Math.PI - phi && radians <= 2 * Math.PI) ||
(radians >= 0 && radians <= phi)
) {
x = width / 2;
y = Math.tan(radians) * x;
} else if (radians >= phi && radians <= Math.PI - phi) {
y = height / 2;
x = y / Math.tan(radians);
} else if (radians >= Math.PI - phi && radians <= Math.PI + phi) {
x = -width / 2;
y = Math.tan(radians) * x;
} else if (radians >= Math.PI + phi && radians <= 2 * Math.PI - phi) {
y = -height / 2;
x = y / Math.tan(radians);
}
return {
x: -Math.round(x),
y: Math.round(y)
};
}
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
const rect1 = new Konva.Rect({
x: 20,
y: 20,
width: 50,
height: 50,
fill: 'green',
draggable: true
});
layer.add(rect1);
const rect2 = new Konva.Rect({
x: 220,
y: 220,
width: 50,
height: 50,
fill: 'red',
draggable: true
});
layer.add(rect2);
const line = new Konva.Line({
stroke: 'black'
});
layer.add(line);
function getCenter(node) {
return {
x: node.x() + node.width() / 2,
y: node.y() + node.height() / 2
}
}
function getPoints(r1, r2) {
const c1 = getCenter(r1);
const c2 = getCenter(r2);
const dx = c1.x - c2.x;
const dy = c1.y - c2.y;
const angle = Math.atan2(-dy, dx);
const startOffset = getRectangleBorderPoint(angle + Math.PI, rect1.size());
const endOffset = getRectangleBorderPoint(angle, rect2.size());
const start = {
x: c1.x - startOffset.x,
y: c1.y - startOffset.y
};
const end = {
x: c2.x - endOffset.x,
y: c2.y - endOffset.y
};
return [start.x, start.y, end.x, end.y]
}
function updateLine() {
const points = getPoints(rect1, rect2);
line.points(points);
}
updateLine();
layer.on('dragmove', updateLine);
layer.draw();
<script src="https://unpkg.com/konva@^3/konva.min.js"></script>
<div id="container"></div>
推荐阅读
- c - Relocation of data from flash to RAM during boot phase
- c# - Get the realtime CPU speed in C#?
- python - Check if the sum of 2 numbers in a list is present in another list in python
- mysql - 如何为 ExcelforMySQL 添加到 Ionos 数据库的远程连接?
- postgresql - 几天后,GCP Postgresql 实例性能变得异常缓慢
- java - 我需要连续运行我的自动化测试用例。最好的方法是什么?
- visual-studio - VS Studio 中的 Terraform 代码不起作用
- python - Python在没有睡眠程序的情况下等待x秒?
- clang-tidy - Clang-Tidy:在添加参数时解决 No Match 错误
- django - django按参数过滤