У меня есть некоторый код JavaScript, который генерирует SVG-пути для подключения HTML-элементов на странице.Это показывает, какой блок подключен к другому блоку.
В путях используются маркеры, поэтому у меня может быть стрелка на конце, чтобы показать его направление.
В целом, это работает хорошо.Я пытался добавить 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
координаты для пути следующим образом:
// Straight line from XY Start to XY End
path.setAttribute(
"d",
"M" + startX + " " + startY + " L" + (endX-30) + " " + endY
);
Говоря об окончании короткого 30, я ожидал, что стрелка не совсем доходит до середины.
Ну, это прекрасно работает для горизонтальных линий, но когда у меня диагональ,помещает его -30
слева от средней точки, как ему было сказано, когда действительно нужно учитывать направление и корректировать Y
.
Горизонтальная линия в порядке с-30
, но диагональная не там, где я хочу (понимая, где я СКАЗАЛ, чтобы она была).
Вот пример того, где я мог бы ожидать, что диагональная линия заканчивается в верхнем левом углу:
Как можно настроить XY
в зависимости от направления пути или есть что-то большее?
JS Fiddle: http://jsfiddle.net/m4fupk7g/5/