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);
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With