Javascript SVG基于路径方向控制X Y

时间:2019-01-31 15:52:32

标签: javascript svg

我有一些JavaScript代码可以生成SVG路径来连接页面上的HTML元素。此显示哪个盒子连接到另一个盒子。

路径正在使用标记,因此我可以在末端使用箭头显示其方向。

总体来说,这很好。我一直在尝试向此设置中添加一个str2num,它直接位于开始和结束元素的中心。

我的目标是使路径停止在该圆的附近,以使箭头指向它,而不是在其顶部。

示例屏幕截图: enter image description here

代码示例:

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的水平线很好,但是对角线不是我想要的位置(理解它告诉我的位置)。

enter image description here

以下是我希望看到对角线结束于左上方方框的示例:

enter image description here

我该如何根据路径的方向调整-30呢?

JS小提琴: http://jsfiddle.net/m4fupk7g/5/

1 个答案:

答案 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;