对于给定的 D3 强制图,我面临两个视觉问题。
\n我感谢任何让我更接近结果的链接、提示、帮助。
\n var width = window.innerWidth,\n height = window.innerHeight;\n\n var svg = d3.select("body").append("svg")\n .attr("width", width)\n .attr("height", height)\n .call(d3.zoom().on("zoom", function(event) {\n svg.attr("transform", event.transform)\n }))\n .append("g")\n\n ////////////////////////\n // outer force layout\n\n var data = {\n "nodes":[\n { "id": "A" }, \n { "id": "B" },\n { "id": "C" },\n ],\n "links": [\n { "source": "A", "target": "B"},\n { "source": "B", "target": "C"},\n { "source": "C", "target": "A"}\n ]\n };\n\n var simulation = d3.forceSimulation()\n .force("size", d3.forceCenter(width / 2, height / 2))\n .force("charge", d3.forceManyBody().strength(-1000))\n .force("link", d3.forceLink().id(function (d) { return d.id }).distance(250))\n\n svg.append("defs").append("marker")\n .attr(\'id\', \'arrowhead\')\n .attr(\'viewBox\', \'-0 -5 10 10\')\n .attr("refX", 23.5)\n .attr("refY", 0)\n .attr(\'orient\', \'auto\')\n .attr(\'markerWidth\', 10)\n .attr(\'markerHeight\', 10)\n .attr("orient", "auto")\n .append(\'svg:path\')\n .attr(\'d\', \'M 0,-5 L 10 ,0 L 0,5\')\n .attr(\'fill\', \'#999\')\n .style(\'stroke\', \'none\');\n\n var links = svg.selectAll(".links")\n .data(data.links)\n .join("line")\n .attr("stroke", "red")\n .attr("stroke-width", 3)\n .attr("marker-end", "url(#arrowhead)")\n \n var nodes = svg.selectAll("g.outer")\n .data(data.nodes, function (d) { return d.id; })\n .enter()\n .append("g")\n .attr("class", "outer")\n .attr("id", function (d) { return d.id; })\n .call(d3.drag()\n .on("start", dragStarted)\n .on("drag", dragged)\n .on("end", dragEnded)\n );\n\n nodes\n .append("circle")\n .style("fill", "lightgrey")\n .style("stroke", "blue")\n .attr("r", 40)\n .on("mouseenter", function() {\n d3.select(this)\n .transition()\n .duration(250)\n .attr("r", 40 * 1.3)\n .attr("fill", "blue")\n })\n .on("mouseleave", function() {\n d3.select(this)\n .transition()\n .duration(250)\n .attr("r", 40)\n .attr("fill", "lightgrey")\n }) \n\n simulation\n .nodes(data.nodes)\n .on("tick", tick)\n\n simulation\n .force("link")\n .links(data.links)\n\n\n \n function tick() {\n links\n .attr("x1", function (d) { return d.source.x; })\n .attr("y1", function (d) { return d.source.y; })\n .attr("x2", function (d) { return d.target.x; })\n .attr("y2", function (d) { return d.target.y; });\n \n\n nodes\n .attr("transform", d => `translate(${d.x}, ${d.y})`);\n }\n\n function dragStarted(event, d) {\n if (!event.active) simulation.alphaTarget(0.3).restart();\n d.fx = d.x;\n d.fy = d.y;\n }\n\n function dragged(event, d) {\n d.fx = event.x;\n d.fy = event.y;\n }\n\n function dragEnded(event, d) {\n if (!event.active) simulation.alphaTarget(0);\n d.fx = null;\n d.fy = null;\n }Run Code Online (Sandbox Code Playgroud)\r\n body {\n background: whitesmoke,\xc2\xb4;\n overflow: hidden;\n margin: 0px;\n }Run Code Online (Sandbox Code Playgroud)\r\n<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset="utf-8">\n <meta name="viewport" content="width=device-width, initial-scale=1">\n\n <title>D3v7</title>\n <!-- d3.js framework -->\n <script src="https://d3js.org/d3.v7.js"></script>\n</head>\n\n\n\n<body>\n\n</body>\n\n</html>Run Code Online (Sandbox Code Playgroud)\r\n目前我能想到两种解决方案。
\n<path(.links)/>向量并减去扩展圆半径<marker />我个人选择了第二种解决方案;它需要更少的计算并且更容易理解。
\n为每个链接添加单独的箭头,因为 svg 路径似乎不支持单独的偏移量。
\n // add custom arrowheads for each link\n // to avoid exhaustive computations\n svg.append("defs").selectAll(\'marker\')\n .data(data.links)\n .join("marker")\n .attr(\'id\', d => `arrowhead-target-${d.target}`)\n// ...\n var links = svg.selectAll(".links")\n .data(data.links)\n .join("line")\n .attr("stroke", "red")\n .attr("stroke-width", 3)\n .attr("marker-end", d => `url(#arrowhead-target-${d.target})`)\n // add source and target so that it can be traced back\nRun Code Online (Sandbox Code Playgroud)\n的偏移量<marker />应用于.refX, .refY属性,如MDN 文档中所述
nodes\n .append("circle")\n .style("fill", "lightgrey")\n .style("stroke", "blue")\n .attr("r", 40)\n .on("mouseenter", function(ev, d) {\n // trace back to custom arrowhead with given id\n const arrowHead = d3.select(`#arrowhead-target-${d.id}`)\n arrowHead.transition().duration(250).attr(\'refX\', 40)\n d3.select(this)\n .transition()\n .duration(250)\n .attr("r", 40 * 1.3)\n .attr("fill", "blue")\n })\n .on("mouseleave", function(ev, d) {\n const arrowHead = d3.select(`#arrowhead-target-${d.id}`)\n arrowHead.transition().duration(250).attr(\'refX\', 23.5)\n d3.select(this)\n .transition()\n .duration(250)\n .attr("r", 40)\n .attr("fill", "lightgrey")\n }) \nRun Code Online (Sandbox Code Playgroud)\n尝试运行下面的代码。扩展和减法的大小被故意夸大,以便您可以看到差异。
\nvar width = window.innerWidth,\n height = window.innerHeight;\n\n var svg = d3.select("body").append("svg")\n .attr("width", width)\n .attr("height", height)\n .call(d3.zoom().on("zoom", function(event) {\n svg.attr("transform", event.transform)\n }))\n .append("g")\n\n ////////////////////////\n // outer force layout\n\n var data = {\n "nodes":[\n { "id": "A" }, \n { "id": "B" },\n { "id": "C" },\n ],\n "links": [\n { "source": "A", "target": "B"},\n { "source": "B", "target": "C"},\n { "source": "C", "target": "A"}\n ]\n };\n\n var simulation = d3.forceSimulation()\n .force("size", d3.forceCenter(width / 2, height / 2))\n .force("charge", d3.forceManyBody().strength(-1000))\n .force("link", d3.forceLink().id(function (d) { return d.id }).distance(250))\n // add custom arrowheads for each link\n // to avoid exhaustive computations\n svg.append("defs").selectAll(\'marker\')\n .data(data.links)\n .join("marker")\n .attr(\'id\', d => `arrowhead-target-${d.target}`)\n .attr(\'viewBox\', \'-0 -5 10 10\')\n .attr("refX", 23.5)\n .attr("refY", 0)\n .attr(\'orient\', \'auto\')\n .attr(\'markerWidth\', 10)\n .attr(\'markerHeight\', 10)\n .attr("orient", "auto")\n .append(\'svg:path\')\n .attr(\'d\', \'M 0,-5 L 10 ,0 L 0,5\')\n .attr(\'fill\', \'#999\')\n .style(\'stroke\', \'none\');\n\n var links = svg.selectAll(".links")\n .data(data.links)\n .join("line")\n .attr("stroke", "red")\n .attr("stroke-width", 3)\n .attr("marker-end", d => `url(#arrowhead-target-${d.target})`)\n // add source and target so that it can be traced back\n .attr(\'data-source\', d => d.source)\n .attr(\'data-target\', d => d.target)\n \n var nodes = svg.selectAll("g.outer")\n .data(data.nodes, function (d) { return d.id; })\n .enter()\n .append("g")\n .attr("class", "outer")\n .attr("id", function (d) { return d.id; })\n .call(d3.drag()\n .on("start", dragStarted)\n .on("drag", dragged)\n .on("end", dragEnded)\n );\n\n nodes\n .append("circle")\n .style("fill", "lightgrey")\n .style("stroke", "blue")\n .attr("r", 40)\n .on("mouseenter", function(ev, d) {\n // trace back to custom arrowhead with given id\n const arrowHead = d3.select(`#arrowhead-target-${d.id}`)\n arrowHead.transition().duration(250).attr(\'refX\', 40)\n d3.select(this)\n .transition()\n .duration(250)\n .attr("r", 40 * 1.3)\n .attr("fill", "blue")\n })\n .on("mouseleave", function(ev, d) {\n const arrowHead = d3.select(`#arrowhead-target-${d.id}`)\n arrowHead.transition().duration(250).attr(\'refX\', 23.5)\n d3.select(this)\n .transition()\n .duration(250)\n .attr("r", 40)\n .attr("fill", "lightgrey")\n }) \n\n simulation\n .nodes(data.nodes)\n .on("tick", tick)\n\n simulation\n .force("link")\n .links(data.links)\n\n\n \n function tick() {\n links\n .attr("x1", function (d) { return d.source.x; })\n .attr("y1", function (d) { return d.source.y; })\n .attr("x2", function (d) { return d.target.x; })\n .attr("y2", function (d) { return d.target.y; });\n \n\n nodes\n .attr("transform", d => `translate(${d.x}, ${d.y})`);\n }\n\n function dragStarted(event, d) {\n if (!event.active) simulation.alphaTarget(0.3).restart();\n d.fx = d.x;\n d.fy = d.y;\n }\n\n function dragged(event, d) {\n d.fx = event.x;\n d.fy = event.y;\n }\n\n function dragEnded(event, d) {\n if (!event.active) simulation.alphaTarget(0);\n d.fx = null;\n d.fy = null;\n }Run Code Online (Sandbox Code Playgroud)\r\nbody {\n background: whitesmoke,\xc2\xb4;\n overflow: hidden;\n margin: 0px;\n }Run Code Online (Sandbox Code Playgroud)\r\n<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset="utf-8">\n <meta name="viewport" content="width=device-width, initial-scale=1">\n\n <title>D3v7</title>\n <!-- d3.js framework -->\n <script src="https://d3js.org/d3.v7.js"></script>\n</head>\n\n\n\n<body>\n\n</body>\n\n</html>Run Code Online (Sandbox Code Playgroud)\r\n