本文介绍了箭头不接触d3.js中的节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 29岁程序员,3月因学历无情被辞! 我在d3v4中创建了强制布局。在代表方向的链接上添加的标记如Jfiddle所示 https://jsfiddle.net/rjyk72ea/ 要求是箭头应该触及节点,但是当链接在节点的对角方向时,箭头隐藏在节点下(部分或完全)。如何解决这个问题?I created force layout in d3v4. Markers added on links to represent direction as shown in given Jfiddle https://jsfiddle.net/rjyk72ea/Requirement is arrows should be touching to node but Arrows hides below node(partially or completely) when link is in diagonal direction of node.how to resolve this issue?var mark = diagramLayout.append("svg:defs").selectAll("marker")// .data(["end"]) // Different link/path types can be defined here .enter().append("svg:marker") // This section adds in the arrows .attr("id", String) .attr("viewBox", "0 -5 10 10") .attr("refX", markerRefx) .attr("refY", 0) .attr("markerWidth", 5) .attr("markerHeight", 5) .attr("orient", "auto") .attr("stroke", "#000") .attr("fill", "#000") .append("svg:path") .attr("d", "M0,-5L10,0L0,5") .style("stroke-width", "0.3px")}推荐答案您的圆角方块有问题。您有两种选择:将他们视为圈子,然后按照我在此问题中的说法。或者将它们视为矩形(正方形),并找到与正方形的交点。因为,我已经给出了以前的回答为圆,让我们谈论正方形(矩形)/线交叉。Your rounded squares are problematic. You have two choices: treat them as circles and do as I did in this question. Or treat them as rectangles (squares) and find the intersection point with the square. Since, I've already given a previous answer for circles, let's talk about square (rectangle)/line intersection.现在,我不会深入到矩形/线交叉的数学的细节(有很多资源,google搜索),所以让我们开始该功能来自这个伟大的答案(它值得更多的upvotes),并将其应用于您的问题。Now, I won't go into the detail of the math behind rectangle/line intersection (there's lots of resources a google search away for that), so let's start with the function from this great answer (it deserves way more upvotes) and apply it to your question.首先,我改变你的链接代码以使用svg 路径而不是行。只是更清洁和更容易在我看来。使用我在上面链接的函数,然后变得像下面这样容易:First, I change your links code to work with an svg path instead of a line. Just cleaner and easier in my opinion. Using the function I linked above this then becomes as easy as: function ticked(e) { link.attr("d", function(d) { var inter = pointOnRect(d.source.x, d.source.y, d.target.x - 20, d.target.y - 20, d.target.x + 20, d.target.y + 20); return "M" + d.source.x + "," + d.source.y + "L" + inter.x + "," + inter.y; }); ....这是完整的运行代码:<!DOCTYPE html><html><head> <script data-require="[email protected]" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script> <style> .node { stroke: #fff; stroke-width: 1.5px; } .link { stroke: #000; stroke-opacity: .6; } </style></head><body> <div id="mainScreen" style="height:100%;width:100%;position:absolute;"> <svg id="diagramLayout" style="height:100%;width:100%;position:absolute;"></svg> </div> <script> var width = 500; var height = 500; var nodeWidth = 40; var nodeHeight = 40; var circleRadius = 5; var diagramLayout; var graphData = { "nodes": [{ "uid": "Term20", "name": "Term20", "image": "images/Term.png" }, { "uid": "glossforArrow", "name": "glossforArrow", "image": "images/Glossary.png" }, { "uid": "Term43", "name": "Term43", "image": "images/Term.png" }, { "uid": "Term1", "name": "Term43", "image": "images/Term.png" }, { "uid": "Term2", "name": "Term43", "image": "images/Term.png" }], "links": [{ "source": "glossforArrow", "target": "Term20", "direction": "output", "label": "Owned Terms" }, { "source": "glossforArrow", "target": "Term43", "direction": "output", "label": "Owned Terms" }, { "source": "glossforArrow", "target": "Term1", "direction": "output", "label": "Owned Terms" }, { "source": "glossforArrow", "target": "Term2", "direction": "output", "label": "Owned Terms" }] }; forceInitialize(graphData) function forceInitialize(graphData) { diagramLayout = d3.select("#diagramLayout") .attr("id", "diagramLayout") //set id .attr("width", width) //set width .attr("height", height) //set height .append("g") .attr("transform", "translate(" + 20 + "," + 20 + ")") markerRefx = 35; simulation = d3.forceSimulation(); alphaMulti = 1; simulation.force("link", d3.forceLink().id(function(d) { return d.uid; }).distance(70).strength(0)) .force("charge", d3.forceManyBody().distanceMin(20).distanceMax(50)) .force("centre", d3.forceCenter(width / 2, height / 2)) .force("x", d3.forceX(2)) .force("y", d3.forceY(10)) .force("collide", d3.forceCollide().radius(function(d) { return 80; }).iterations(2)) simulation.on('end', function() { simulation.force("link", d3.forceLink().id(function(d) { return d.uid; }).distance(30).strength(0.0).iterations(10)) .force("x", d3.forceX().strength(0)) .force("y", d3.forceX().strength(0)) }); force(graphData); } //Force Layout function force(graphData) { var linkEnter = diagramLayout.selectAll(".links"); linkEnter = linkEnter.data(graphData.links) .enter().append("g") .attr("class", "links") var link = linkEnter.append("path") .attr("stroke-width", function(d) { return Math.sqrt(2); }) .attr("stroke-opacity", "0.3") .attr("stroke", "#000") graphData.links.forEach(function(d) { if (d.direction == "input") { var mark = diagramLayout.append("svg:defs").selectAll("marker") // .data(["start"]) // Different link/path types can be defined here .enter().append("svg:marker") // This section adds in the arrows .attr("id", String) .attr("viewBox", "0 -5 10 10") .attr("refX", 0) .attr("refY", 0) .attr("markerWidth", 5) .attr("markerHeight", 5) .attr("orient", "auto") .attr("stroke", "#000") .attr("fill", "#000") .append("svg:path") .attr("d", "M0,-5L10,0L0,5") .style("stroke-width", "0.3px") .attr("transform", "rotate(180,5, 0)"); } else if (d.direction == "output") { var mark = diagramLayout.append("svg:defs").selectAll("marker") // .data(["end"]) // Different link/path types can be defined here .enter().append("svg:marker") // This section adds in the arrows .attr("id", String) .attr("viewBox", "0 -5 10 10") .attr("refX", 9) .attr("refY", 0) .attr("markerWidth", 5) .attr("markerHeight", 5) .attr("orient", "auto") .attr("stroke", "#000") .attr("fill", "#000") .append("svg:path") .attr("d", "M0,-5L10,0L0,5") .style("stroke-width", "0.3px") } }); link.attr("marker-end", function(d) { if (d.direction === "input") return ""; else return "url(#end)"; }) link.attr("marker-start", function(d) { if (d.direction === "input") return "url(#start)"; else return ""; }) var node = diagramLayout.selectAll(".node"); node = node.data(graphData.nodes, function(d) { return d.uid; }); var nodeEnter = node.enter().append("g") .attr("class", "node") .attr("height", nodeHeight) .attr("width", nodeWidth) var nodeIcon = nodeEnter.append("rect") .attr("class", "rect") .attr("x", -20) .attr("y", -20) .attr("rx", 10) .attr("width", 40) .attr("height", 40) .attr("stroke-width", function(d) { return Math.sqrt(2); }) .attr("stroke-opacity", "0.3") .attr("stroke", "#000") .attr("fill", "steelblue") nodeIcon.call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); simulation .nodes(graphData.nodes) .on("tick", ticked); setTimeout(function tick() { simulation.tick(); if (simulation.alpha() >= .005); setTimeout(tick, 0); }, 0); simulation.force("link") .links(graphData.links); simulation.restart(); function ticked(e) { link.attr("d", function(d) { var inter = pointOnRect(d.source.x, d.source.y, d.target.x - 20, d.target.y - 20, d.target.x + 20, d.target.y + 20); return "M" + d.source.x + "," + d.source.y + "L" + inter.x + "," + inter.y; }) nodeEnter.attr("transform", function(d) { d.fixed = true; return "translate(" + d.x + "," + d.y + ")"; }); } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { d3.select(this).classed("fixed", d.fixed = false); d3.selectAll(".node").fixed = true; } /** * Finds the intersection point between * * the rectangle * with parallel sides to the x and y axes * * the half-line pointing towards (x,y) * originating from the middle of the rectangle * * Note: the function works given min[XY] <= max[XY], * even though minY may not be the "top" of the rectangle * because the coordinate system is flipped. * * @param (x,y):Number point to build the line segment from * @param minX:Number the "left" side of the rectangle * @param minY:Number the "top" side of the rectangle * @param maxX:Number the "right" side of the rectangle * @param maxY:Number the "bottom" side of the rectangle * @param check:boolean (optional) whether to treat point inside the rect as error * @return an object with x and y members for the intersection * @throws if check == true and (x,y) is inside the rectangle * @author TWiStErRob * @see <a href="http://stackoverflow.com/a/31254199/253468">source</a> * @see <a href="http://stackoverflow.com/a/18292964/253468">based on</a> */ function pointOnRect(x, y, minX, minY, maxX, maxY, check) { //assert minX <= maxX; //assert minY <= maxY; if (check && (minX <= x && x <= maxX) && (minY <= y && y <= maxY)) throw "Point " + [x, y] + "cannot be inside " + "the rectangle: " + [minX, minY] + " - " + [maxX, maxY] + "."; var midX = (minX + maxX) / 2; var midY = (minY + maxY) / 2; // if (midX - x == 0) -> m == ±Inf -> minYx/maxYx == x (because value / ±Inf = ±0) var m = (midY - y) / (midX - x); if (x <= midX) { // check "left" side var minXy = m * (minX - x) + y; if (minY <= minXy && minXy <= maxY) return { x: minX, y: minXy }; } if (x >= midX) { // check "right" side var maxXy = m * (maxX - x) + y; if (minY <= maxXy && maxXy <= maxY) return { x: maxX, y: maxXy }; } if (y <= midY) { // check "top" side var minYx = (minY - y) / m + x; if (minX <= minYx && minYx <= maxX) return { x: minYx, y: minY }; } if (y >= midY) { // check "bottom" side var maxYx = (maxY - y) / m + x; if (minX <= maxYx && maxYx <= maxX) return { x: maxYx, y: maxY }; } // Should never happen :) If it does, please tell me! throw "Cannot find intersection for " + [x, y] + " inside rectangle " + [minX, minY] + " - " + [maxX, maxY] + "."; } } </script></body></html> 这篇关于箭头不接触d3.js中的节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!
08-21 05:38