Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3 force layout nodes always start at top-left of screen

Tags:

d3.js

I have a force layout with nodes defined as follows...

nodes = someData.map(function (d) {
        return {
            image_location: d.image_location, 
            image_width: d.image_width, 
            image_height: d.image_height, 
            aspect_ratio: d.aspect_ratio,
            radius: circleRadiusScale(d.someCount),
            x: width/2,
            y: height / 2,
            px: width/2,
            py: height/2,
            cx: width/2,
            cy: height / 2
        };
    });     

And then the force layout being created later in the code with...

force = d3.layout.force()
    .gravity(.05)
    .alpha(0.1) 
    .size([width, height])
    .on("tick", tick); 

force.nodes(nodes)
      .start();

The reason I've forced the x/y, px/py, cx/cy values is that I'm simply trying to ensure that the nodes don't always start being projected at the top left of the browser (which is only for a split second until the force simulation takes affect, but is pretty noticeable especially on firefox or mobile devices).

What's strange to me is that I'm starting the force layout with my x/y, cx/cy, px/py values BEFORE the code I've written joining circles, images etc that the browser should display - in other words, this code comes AFTER defining/starting the force layout...

node = svg.selectAll(".node")
              .data(force.nodes(), function(d) { return d.id; })
              .enter()           
              .append("g")
              .attr("class", "node") etc to append circles, images...

So I'm wondering how to hold back the projection of the nodes by the browser until I know the force layout has an initial position of something other than 0,0. Thanks for any thoughts!

like image 335
d3wannabe Avatar asked Oct 20 '15 16:10

d3wannabe


2 Answers

I would check the position inside the tick handler function and initialise once the condition has been met:

var initialized = false;
force.on("tick", function() {
  if(!initialized) {
    // can also check multiple nodes here...
    if(nodes[0].x != width/2 && nodes[0].y != height/2) initialize();
  } else {
    // update node positions
  }
});

function initialize() {
  node = svg.selectAll(".node")
          .data(force.nodes(), function(d) { return d.id; })
          .enter()           
          .append("g")
          .attr("class", "node") // etc
  initialized = true;
}
like image 166
Lars Kotthoff Avatar answered Nov 07 '22 14:11

Lars Kotthoff


In my case, nodes inited and displayed before call tick(). So they will display at top left of screen firstly, after that call tick() to translate to real position in graph. Just hidden node when add graphic:

nodeEnter.style("visibility", "hidden")

And, visible in tick() function:

nodeEnter.style("visibility", "visible")
like image 26
BlackDen Avatar answered Nov 07 '22 13:11

BlackDen