Force directed graph: zoomable

This example builds off the d3-zoom articles to add zoom functionality onto our force directed graph. If you’ve got questions on how zoom works or what this code is really doing, check those articles out!

Once you know what’s going on, adding zoom to force directed graph is really simple.

Most of the code in the above example (click on the picture to see it) I’ve covered in other articles, so I won’t go over it again.

The key things to note are as follows:

Adding the zoom handler Link to heading

What I refer to as the “zoom handler” is simply a variable to store the function that is applied on SVG elements to allow them to have zoom functionality.

In this example we only need one zoom handler, and we apply it onto the backing SVG element so that it will trigger whenever and wherever we mouse-wheel over the graph.

//add zoom capabilities 
var zoom_handler = d3.zoom()
    .on("zoom", zoom_actions);

zoom_handler(svg);     

function zoom_actions(){
    g.attr("transform", d3.event.transform)
}

Zooming on a “g” element Link to heading

The key point to note is that we create a g element that contains both the nodes and the lines.

This means that any SVG coordinate transforms that we apply will apply to the entire force directed graph, and not subsets of it. You can imagine that if we applied the zoom handler to a g element only containing the nodes, we’d get strange behaviour where the nodes zoom in and the links didn’t. No thanks.

This also prevents unwanted behaviour from occurring when combining zoom and drag together. Read this article for more on the subject.

Anyway, this bit of the code is like this:

//add encompassing group for the zoom 
var g = svg.append("g")
    .attr("class", "everything");

//draw lines for the links 
var link = g.append("g")
      .attr("class", "links")
    .selectAll("line")
    .data(links_data)
    .enter().append("line")
      .attr("stroke-width", 2)
      .style("stroke", linkColour);        

//draw circles for the nodes 
var node = g.append("g")
        .attr("class", "nodes") 
        .selectAll("circle")
        .data(nodes_data)
        .enter()
        .append("circle")
        .attr("r", radius)
        .attr("fill", circleColour);

That’s it - it’s really not that hard at all. Happy zooming!


Hope you found that useful! Click here to view to the rest of the force directed graph series.