Reputation: 4180
I have a d3v4 force-directed graph. For simplicity's sake, let's assume it's like this.
The graph shows flows (like a powergrid). I'd love to be able to animate each link to show flow from the target to the source (with moving dotted lines, bubbles, something).
Does anyone have an idea on how to do that?
Upvotes: 4
Views: 1800
Reputation: 61686
Here is a slightly modified version of Mike's "Les Misérables" force directed graph in which lines are composed of dashes for which the offset is periodically incremented (inspired by this post):
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().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("https://gist.githubusercontent.com/mbostock/4062045/raw/5916d145c8c048a6e3086915a6be464467391c62/miserables.json", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
var lines = d3.selectAll('line');
var offset = 1;
setInterval(function() {
lines.style('stroke-dashoffset', offset);
offset += 1;
}, 50);
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return 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) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
line {
opacity: 0.5;
stroke-dasharray: 10, 4;
}
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
Once the graph is created, we can start a periodic update of the dash offset:
var lines = d3.selectAll('line');
// Updates the offset of dashes every 50ms:
var offset = 1;
setInterval( function() {
lines.style('stroke-dashoffset', offset);
offset += 1;
}, 50);
And here is the css associated to the dashed line:
line {
opacity: 0.5;
stroke-dasharray: 10, 4;
}
Upvotes: 3