Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to zoom into a D3 force layout graph?

D3 has a force directed layout here. Is there a way to add zooming to this graph? Currently, I was able to capture the mouse wheel event but am not really sure how to write the redraw function itself. Any suggestions?

var vis = d3.select("#graph")   .append("svg:svg")   .call(d3.behavior.zoom().on("zoom", redraw)) // <-- redraw function   .attr("width", w)   .attr("height", h); 
like image 270
Legend Avatar asked Oct 24 '11 04:10

Legend


2 Answers

Update 6/4/14

See also Mike Bostock's answer here for changes in D3 v.3 and the related example. I think this probably supersedes the answer below.

Update 2/18/2014

I think @ahaarnos's answer is preferable if you want the entire SVG to pan and zoom. The nested g elements in my answer below are really only necessary if you have non-zooming elements in the same SVG (not the case in the original question). If you do apply the behavior to a g element, then a background rect or similar element is required to ensure that the g receives pointer events.

Original Answer

I got this working based on the zoom-pan-transform example - you can see my jsFiddle here: http://jsfiddle.net/nrabinowitz/QMKm3/

It was a bit more complex than I had hoped - you have to nest several g elements to get it to work, set the SVG's pointer-events attribute to all, and then append a background rectangle to receive the pointer events (otherwise it only works when the pointer is over a node or link). The redraw function is comparatively simple, just setting a transform on the innermost g:

var vis = d3.select("#chart")   .append("svg:svg")     .attr("width", w)     .attr("height", h)     .attr("pointer-events", "all")   .append('svg:g')     .call(d3.behavior.zoom().on("zoom", redraw))   .append('svg:g');  vis.append('svg:rect')     .attr('width', w)     .attr('height', h)     .attr('fill', 'white');  function redraw() {   console.log("here", d3.event.translate, d3.event.scale);   vis.attr("transform",       "translate(" + d3.event.translate + ")"       + " scale(" + d3.event.scale + ")"); } 

This effectively scales the entire SVG, so it scales stroke width as well, like zooming in on an image.

There is another example that illustrates a similar technique.

like image 100
nrabinowitz Avatar answered Oct 06 '22 14:10

nrabinowitz


Why the nested <g>'s?

This code below worked well for me (only one <g>, with no random large white <rect>:

var svg = d3.select("body")     .append("svg")       .attr({         "width": "100%",         "height": "100%"       })       .attr("viewBox", "0 0 " + width + " " + height )       .attr("preserveAspectRatio", "xMidYMid meet")       .attr("pointer-events", "all")     .call(d3.behavior.zoom().on("zoom", redraw));  var vis = svg     .append('svg:g');  function redraw() {   vis.attr("transform",       "translate(" + d3.event.translate + ")"       + " scale(" + d3.event.scale + ")"); } 

Where all the elements in your svg are then appended to the vis element.

like image 24
aaaronic Avatar answered Oct 06 '22 15:10

aaaronic