Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animating data motion between nodes in d3

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?

like image 836
singmotor Avatar asked Oct 21 '25 05:10

singmotor


1 Answers

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;
}
like image 159
Xavier Guihot Avatar answered Oct 22 '25 20:10

Xavier Guihot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!