Zoom in d3 version 4: minimal example

In this post we’ll make a minimal example to demonstrate how zoom works.

I’ll use a similar setup to this other example that was used to demonstrate drag -  create circles on the screen at random and then see if we can zoom in on them.

Nice and simple.

Setup Link to heading

First we’ll set up a simple scaffold for the example:

<!DOCTYPE html>
<meta charset="utf-8">
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");
 
//d3 code goes here 
 
</script>

Next we generate data for some sample circles. We specify an x and y position for each of our circles at random.

//create some circles at random points on the screen 
//create 50 circles of radius 20
//specify centre points randomly through the map function 
var radius = 20;
var circle_data = d3.range(50).map(function() {
    return{
        x : Math.round(Math.random() * (width - radius*2 ) + radius),
        y : Math.round(Math.random() * (height - radius*2 ) + radius)
    }; 
}); 

Now we draw them onto the page. Just for fun we’ll use arrow notation to set the x and y attributes, but don’t worry if you don’t know much about it. It’s not important for this example.

//draw the circles on the page 
var circles = svg.append("g")
    .attr("class", "circles")
    .selectAll("circle")
        .data(circle_data)
        .enter()
        .append("circle")
        //same as .attr("cx", function(d) {return(d.x)})
        .attr("cx", (d) => d.x)
        //same as .attr("cy", function(d) {return(d.y)})
        .attr("cy", (d) => d.y)
        .attr("r", radius)
        .attr("fill", "blue");

Now all that’s left is to add zoom.

Zoom Link to heading

We get zoom working through the d3.zoom() function.

This function is used to set up a “zoom handler”, which is basically something that we add to SVG elements. We’ll be able to zoom into anything that the zoom handler is applied to.

So if we apply the zoom handler on a large SVG rectangle, we’ll be able to zoom whenever we mousewheel over the rectangle. Apply the zoom handler to some circles - your zoom will only work when you’re mousing over those circles.

We often combine d3.zoom() with zoom.on(). The job of the zoom.on() function is to facilitate the actual process of the zoom. We use zoom.on() to tell the zoom listener what event to look out for and the actions to take when that event is detected.

In our particular case, we want to get zoom working. Hence, the events we’re looking for are “zoom” events.

What do we do when we see a zoom event? We’re going to call the function zoom_actions, which we’ll write soon.

What triggers a zoom event? Mouse wheels, double clicks, dragging the mouse, that kind of pinching gesture you do on your laptop trackpad - all of them count as zoom events. The API reference has the complete list of events and how they correspond to D3’s zoom events.

//create zoom handler 
//zoom actions is a function that performs the zooming. 
var zoom_handler = d3.zoom()
    .on("zoom", zoom_actions);

Say a zoom event is detected, like a mouse wheel. What happens next?

One thing that happens is that d3.event gets set to the “zoom event”. The zoom event has these fields:

  • target: the zoom behaviour of the event (essentially our zoom handler)
  • sourceEvent: what kind of event triggered the zoom (like a WheelEvent for a mousewheel, or a MouseEvent if you doubleclick)
  • type: the type of event - one of “start”, “zoom” or “end”
  • transform: the current zoom transform

It’s that last one that we’re interested in here - the zoom transform. What’s interesting about the zoom transform is that it gives us three pieces of useful information. All three bits of information are relative to the starting position of the element that the zoom handler is applied to.

Inside the zoom handler:

  • x: how translated the element is along the x-axis
  • y: how translated the element is along the y-axis
  • k: how scaled the element is

These three attributes link back to the previous article on SVG transforms, so give it a read if you’re not sure what translated means or how things get scaled.

Each time we zoom in (or out), this zoom transform gets updated.  What we’re going to do is update the circles so that they represent the internal state of the zoom transform. We’ll set the transform property of the SVG circles to be equal to the zoom transform with the function zoom.transform.

//specify what to do when zoom event listener is triggered 
function zoom_actions(){
  circles.attr("transform", d3.event.transform);
}

Last thing to do is to apply the zoom handler to the SVG canvas. This means that our zoom handler will always pick up on the mouse events, as they can occur anywhere inside the encompassing SVG rectangle.

//add zoom behaviour to the svg element 
//same as svg.call(zoom_handler); 
zoom_handler(svg);

And we’re done. Click the picture at the top of the article to see the final result!


This post is part of a series of articles on how zoom works in version 4 of d3. Hope you enjoyed it!