Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to speed up the force layout animation in d3.js

I am using D3.js for rendering about 500 nodes and links among them. It usually needs 10 seconds for the layout to settle down (the iteration to converge).

How do I speed up the whole process,say, the nodes are moving 2x faster during animation. then the time will be 50% (The CPU time that used for the iteration should be much less than 10 seconds, but how can I reduce the animation time).

I have tried:

  1. manually manage the tick() in a for loop with certain times, say, 100 times, it is faster, but the animation will be hidden from the user, which is a big loss.
  2. Increasing the link strength will be helpful, nodes will move much faster during animation. But the layout is very sensitive, any small drag might cause many nodes move.

Any suggestions? Thanks.

like image 736
Bruce Avatar asked Oct 03 '14 23:10

Bruce


2 Answers

Injecting a call / calls within ticked event handler can be better, the requestAnimationFrame method would cause a weird bug on MacBook with touch pad environment.

function ticked() {
  for (let i = 0; i < 5; i++) {
    force.tick();
  }

  link.attr('x1', (d) => d.source.x)
      .attr('y1', (d) => d.source.y)
      .attr('x2', (d) => d.target.x)
      .attr('y2', (d) => d.target.y);

  node.attr('transform', (d) => `translate(${d.x}, ${d.y})`);
}
like image 107
Dicky Tsang Avatar answered Nov 15 '22 19:11

Dicky Tsang


Check out this thread which has a lot of good info relating to this topic.

One suggestion from that thread that you might try to implement is to call force.tick() several times within a single requestAnimationFrame callback, then update the node and link positions, and then loop until force.alpha reaches 0 (or whatever you want your alpha threshold to be). Try something like this:

var ticksPerRender = 3;

requestAnimationFrame(function render() {

  for (var i = 0; i < ticksPerRender; i++) {
    force.tick();
  }

  // UPDATE NODE AND LINK POSITIONS HERE

  if (force.alpha() > 0) {
    requestAnimationFrame(render);
  }
});

That would render once for every 3 ticks, or 3x speed. Adjust the ticksPerRender value as needed.

HERE is a simple demo. In this case, I've used the force.on('start', callback) to call the rendering logic described above. This means it will automatically be called again when beginning a drag interaction.

like image 28
jshanley Avatar answered Nov 15 '22 20:11

jshanley