我有一些JavaScript代码可以生成SVG路径来连接页面上的HTML元素。此显示哪个盒子连接到另一个盒子。
路径正在使用标记,因此我可以在末端使用箭头显示其方向。
总体来说,这很好。我一直在尝试向此设置中添加一个str2num
,它直接位于开始和结束元素的中心。
我的目标是使路径停止在该圆的附近,以使箭头指向它,而不是在其顶部。
代码示例:
circle
我的尝试:
我尝试做的一件事是像这样调整路径的最终//helper functions, it turned out chrome doesn't support Math.sgn()
function signum(x) {
return (x < 0) ? -1 : 1;
}
function absolute(x) {
return (x < 0) ? -x : x;
}
/**
* Get the offsets of page elements
* @param el
*/
function getOffset(el) {
const rect = el.getBoundingClientRect();
return {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
bottom: rect.bottom - window.pageYOffset,
width: rect.width || el.offsetWidth,
height: rect.height || el.offsetHeight
};
}
/**
* Draw the path on the SVG using proided coords
* @param svg
* @param path
* @param startX
* @param startY
* @param endX
* @param endY
*/
function drawPath(svg, path, startX, startY, endX, endY, circle) {
// Get the path's stroke width (if one wanted to be really precize, one could use half the stroke size)
const style = getComputedStyle(path);
const stroke = parseFloat(style.strokeWidth);
// Check if the svg is big enough to draw the path, if not, set height/width
if (svg.getAttribute("height") < startY) {
svg.setAttribute("height", startY + 20);
}
if (svg.getAttribute("width") < startX + stroke) {
svg.setAttribute("width", startX + stroke + 20);
}
if (svg.getAttribute("width") < endX + stroke) {
svg.setAttribute("width", endX + stroke + 20);
}
/**
M = moveto
L = lineto
H = horizontal lineto
V = vertical lineto
C = curveto
S = smooth curveto
Q = quadratic Bézier curve
T = smooth quadratic Bézier curveto
A = elliptical Arc
Z = closepath
*/
// Straight line from XY Start to XY End
path.setAttribute(
"d",
"M" + startX + " " + startY + " L" + endX + " " + endY
);
// Show the starting and ending circle
if (circle) {
circle.setAttribute("cx", startX);
circle.setAttribute("cy", startY);
circle.setAttribute("cx", endX);
circle.setAttribute("cy", endY);
}
}
/**
* Calculate the coords for where the line will be drawn
* @param svg
* @param path
* @param startElem
* @param endElem
* @param type
*/
function connectElements(svg, path, startElem, endElem, circle) {
// Define our container
const svgContainer = document.getElementById("svgContainer"),
svgTop = getOffset(svgContainer).top,
svgLeft = getOffset(svgContainer).left,
startCoord = startElem,
endCoord = endElem;
let startX, startY, endX, endY;
// Calculate path's start (x,y) coords
// We want the x coordinate to visually result in the element's mid point
startX =
getOffset(startCoord).left +
getOffset(startCoord).width / 2 -
svgLeft;
startY =
getOffset(startCoord).top +
getOffset(startCoord).height / 2 -
svgTop;
// Calculate path's start (x,y) coords
// We want the x coordinate to visually result in the element's mid point
endX =
getOffset(endCoord).left +
0.5 * getOffset(endCoord).width -
svgLeft;
endY =
getOffset(endCoord).top +
getOffset(endCoord).height / 2 -
svgTop;
// Call function for drawing the path
drawPath(svg, path, startX, startY, endX, endY, circle);
}
function connectAll() {
// Loop over our destinations
for (let i = 0; i < dest.length; i++) {
// Define
const marker = document.createElementNS(
"http://www.w3.org/2000/svg",
"marker"
);
const path = document.createElementNS(
"http://www.w3.org/2000/svg",
"path"
);
const markerPath = document.createElementNS(
"http://www.w3.org/2000/svg",
"path"
);
const defs = document.createElementNS(
"http://www.w3.org/2000/svg",
"defs"
);
const circle = document.createElementNS(
"http://www.w3.org/2000/svg",
"circle"
);
// Set definitions attribute
defs.setAttribute("id", "defs");
// Create our center circle
circle.setAttribute("id", "circle_" + dest[i].linkID + "_" + dest[i].boxID);
circle.setAttribute("cx", "0");
circle.setAttribute("cy", "0");
circle.setAttribute("r", "15");
circle.setAttribute("fill", "red");
// Append our circle
document.getElementById('svg1').appendChild(circle);
// Set up marker (Arrow)
marker.setAttribute("id", "Triangle");
marker.setAttribute("viewBox", "0 0 10 10");
marker.setAttribute("refX", "0");
marker.setAttribute("refY", "5");
marker.setAttribute("markerUnits", "strokeWidth");
marker.setAttribute("markerWidth", "4");
marker.setAttribute("markerHeight", "3");
marker.setAttribute("orient", "auto");
// Append our marker (Arrow)
marker.appendChild(markerPath);
markerPath.setAttribute("d", "M 0 0 L 10 5 L 0 10 z");
markerPath.setAttribute("fill", "#428bca");
// Create our main path
path.setAttribute(
"id",
"path_" + dest[i].linkID + "_" + dest[i].boxID
);
path.setAttribute("class", "path");
path.setAttribute(
"marker-end",
"url(" + window.location + "#Triangle)"
);
// Only create one set of definitions
if (i === 0) {
document.getElementById('svg1').appendChild(defs);
document.getElementById('defs').appendChild(marker);
}
// Append our path to the SVG
document.getElementById('svg1').appendChild(path);
const svg = document.getElementById("svg1"),
p = document.getElementById(
"path_" + dest[i].linkID + "_" + dest[i].boxID
),
startingBox = document.getElementById("box_" + dest[i].boxID),
destinationBox = document.getElementById(
"box_" + dest[i].destinationBoxID
);
// Connect paths
connectElements(
svg,
p,
startingBox,
destinationBox,
circle
);
}
}
// Define our boxes to connect
var dest = [{
"boxID": "16",
"destinationBoxID": "5",
"linkID": "1"
},
{
"boxID": "18",
"destinationBoxID": "1",
"linkID": "9"
},
{
"boxID": "2",
"destinationBoxID": "5",
"linkID": "8"
}
]
// Run
connectAll()
坐标:
X
通过告诉它在30点结束时,我希望箭头不会一直延伸到中点。
好吧,这对于水平线来说很好用,但是当我有一个对角线时,它会按照其指示将// Straight line from XY Start to XY End
path.setAttribute(
"d",
"M" + startX + " " + startY + " L" + (endX-30) + " " + endY
);
放在中点的左侧,但实际上它需要考虑并调整-30
。
Y
的水平线很好,但是对角线不是我想要的位置(理解它告诉我的位置)。
以下是我希望看到对角线结束于左上方方框的示例:
我该如何根据路径的方向调整-30
呢?
答案 0 :(得分:2)
您需要将线条转换为角度和距离,减少距离,然后再转换回去。例如:
let dx = endX - startX;
let dy = endY - startY;
let angle = Math.atan2(dy, dx);
let distance = Math.sqrt(dx * dx + dy * dy) - 20;
let offsetX = Math.cos(angle) * distance + startX;
let offsetY = Math.sin(angle) * distance + startY;