Make Wavy path act like spring
Make link paths wavy for randomly-generated network.
wave function creates a wavy-like path between source and target link, using coordinate transforamtions.
Other variants
Springy version nudged by @HarryStevens
| license: gpl-3.0 |
Make Wavy path act like spring
Make link paths wavy for randomly-generated network.
wave function creates a wavy-like path between source and target link, using coordinate transforamtions.
Other variants
Springy version nudged by @HarryStevens
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <style> | |
| .links path { | |
| fill: none; | |
| stroke: #999; | |
| stroke-opacity: 0.6; | |
| } | |
| .nodes circle { | |
| stroke: #fff; | |
| stroke-width: 1.5px; | |
| } | |
| </style> | |
| <svg width="900" height="600"></svg> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <script> | |
| var nodes = d3.range(100).map(function(i) { | |
| return { index: i } | |
| }) | |
| var links = d3.range(nodes.length - 1).map(function(i) { | |
| return { source: Math.floor(Math.sqrt(i)), target: i + 1} | |
| }) | |
| var graph = {nodes: nodes, links: links} | |
| var svg = d3.select("svg"), | |
| width = +svg.attr("width"), | |
| height = +svg.attr("height"); | |
| var color = d3.scaleOrdinal(d3.schemeCategory20); | |
| var simulation = d3.forceSimulation() | |
| .force("link", d3.forceLink().distance(50).strength(1)) | |
| .force("charge", d3.forceManyBody()) | |
| .force("center", d3.forceCenter(width / 2, height / 2)); | |
| var link = svg.append("g") | |
| .attr("class", "links") | |
| .selectAll("path") | |
| .data(graph.links) | |
| .enter().append("path") | |
| var node = svg.append("g") | |
| .attr("class", "nodes") | |
| .selectAll("circle") | |
| .data(graph.nodes) | |
| .enter().append("circle") | |
| .attr("r", function(d) { return 4 + 6*Math.random(); }) | |
| .attr("fill", function(d) { return color(Math.floor(Math.sqrt(d.index))); }) | |
| .call(d3.drag() | |
| .on("start", dragstarted) | |
| .on("drag", dragged) | |
| .on("end", dragended)); | |
| simulation | |
| .nodes(graph.nodes) | |
| .on("tick", ticked); | |
| simulation.force("link") | |
| .links(graph.links); | |
| function ticked() { | |
| link.attr("d", wave); | |
| node | |
| .attr("cx", function(d) { return d.x; }) | |
| .attr("cy", function(d) { return d.y; }); | |
| } | |
| // Create wavy path between source and target | |
| function wave(d) { | |
| var p1 = d.source, p2 = d.target | |
| var dist = Math.hypot(p2.y - p1.y, p2.x - p1.x) | |
| var angle = Math.atan2(p2.y - p1.y, p2.x - p1.x) | |
| var ca = Math.cos(angle), sa = Math.sin(angle) | |
| var step = dist / 80, amp = 3, cpi = step*Math.PI | |
| var arr = [] | |
| for (x=0; x<=dist; x+=step){ | |
| var y = amp * Math.sin(x/cpi) | |
| var x2 = p1.x + x*ca - y*sa | |
| var y2 = p1.y + x*sa + y*ca | |
| arr.push({x:x2, y:y2}) | |
| } | |
| var line = d3.line().curve(d3.curveBasis) | |
| .x(function(d) { return d.x; }) | |
| .y(function(d) { return d.y; }) | |
| return line(arr) | |
| } | |
| 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) { | |
| if (!d3.event.active) simulation.alphaTarget(0); | |
| d.fx = null; | |
| d.fy = null; | |
| } | |
| </script> |