limit panning and zooming

3,180 views
Skip to first unread message

Scott Bronson

unread,
Nov 14, 2011, 6:28:31 AM11/14/11
to d3-js
Is there a way to prevent d3.behavior.zoom() from zooming my graph out
of view?

Maybe there's a way to clamp d3.event.transform, or I can write my
scales to only allow transforms in a certain range?


I have:

d3.select('body').append('svg:svg') ...
.call(d3.behavior.zoom().on("zoom", redraw))

And, of course, redraw changes the scale:

function redraw() {
if (d3.event && d3.event.transform) {
d3.event.transform(x, y);
};

// recompute all shape locations
}


Thanks for any help!

- Scott

Lars Kotthoff

unread,
Nov 14, 2011, 6:37:57 AM11/14/11
to d3...@googlegroups.com, bro...@rinspin.com
> Is there a way to prevent d3.behavior.zoom() from zooming my graph out
> of view?

You could check whether the values are within an acceptable range in the
function that redraws and only do anything at all if they are. This is kind of
hacky though.

Lars

Jason Davies

unread,
Nov 14, 2011, 9:59:34 AM11/14/11
to d3...@googlegroups.com
On Mon, Nov 14, 2011 at 03:28:31AM -0800, Scott Bronson wrote:
> Is there a way to prevent d3.behavior.zoom() from zooming my graph out
> of view?

Yes, you can set the extent for x, y and z as of v2.5.0, e.g.

.call(d3.behavior.zoom()
.extent([
[-Infinity, Infinity],
[-Infinity, Infinity],
[0, Infinity]
])
.on("zoom", redraw));

--
Jason Davies, http://www.jasondavies.com/

Lars Kotthoff

unread,
Nov 14, 2011, 3:08:39 PM11/14/11
to d3...@googlegroups.com, ja...@jasondavies.com
> Yes, you can set the extent for x, y and z as of v2.5.0, e.g.
>
> .call(d3.behavior.zoom()
> .extent([
> [-Infinity, Infinity],
> [-Infinity, Infinity],
> [0, Infinity]
> ])
> .on("zoom", redraw));

Cool! What are the values, projected or original coordinates? I've played around
with this but couldn't get it to work properly :( If I use unprojected
coordinates to set the extent, it breaks when I zoom in, i.e. the extent is much
smaller then than what I set.

Lars

Jason Davies

unread,
Nov 14, 2011, 3:14:29 PM11/14/11
to d3...@googlegroups.com

It uses pixel-space coordinates i.e. projected. The z-range denotes the
allowed zoom levels, so zoom level 1 means everything is doubled in
size, 2 means everything is quadrupled, etc.

Lars Kotthoff

unread,
Nov 14, 2011, 3:44:24 PM11/14/11
to d3...@googlegroups.com, ja...@jasondavies.com
> It uses pixel-space coordinates i.e. projected. The z-range denotes the
> allowed zoom levels, so zoom level 1 means everything is doubled in
> size, 2 means everything is quadrupled, etc.

Ah, ok. I was trying to limit the extent to a section of the map (i.e.
unprojected coordinate bounds). Am I right in thinking that I would need to
reset the extent after each zoom to do that?

Lars

Jason Davies

unread,
Nov 15, 2011, 4:38:30 AM11/15/11
to d3...@googlegroups.com

Hmm, I will have to think about this. It *should* be possible by
setting an extent using the projected coordinate bounds. But it didn't
seem to work, so I'll need to look into it and get back to you. :)

Lars Kotthoff

unread,
Nov 15, 2011, 4:42:32 AM11/15/11
to d3...@googlegroups.com, ja...@jasondavies.com
> Hmm, I will have to think about this. It *should* be possible by
> setting an extent using the projected coordinate bounds. But it
> didn't seem to work, so I'll need to look into it and get back to
> you. :)

Thanks. That seemed like an obvious use case to me (at least I've wanted to do
this). Maybe that should be a separate function though (à la geotransform issue)
to keep the current functionality.

Lars

tandu

unread,
Mar 26, 2012, 4:31:29 PM3/26/12
to d3...@googlegroups.com
I haven't found how to restrict panning. d3 was updated and it doesn't have `extent` method now. It seems that it only can limit scaling with `scaleExtent`. In 'examples/mercator/mercator-zoom-constrained.html' is implemented panning constrain, but it works only for `projection` which has similar `translate` method. How to add pan limits to `d3.time.scale`? Should I calculate new domain and assign it manually? Is there simpler way that I missed?

tandu

unread,
Mar 26, 2012, 4:47:05 PM3/26/12
to d3...@googlegroups.com
I have implemented panning restriction that way:

    var minT = new Date('01/01/2001'), maxT = new Date('01/01/2002'), w = $(window).width();
    function onZoom() {
      if(data.translateExtent) {
        var t = d3.event.translate,
        ext = [x(minT), w - x(maxT)],
        d = x.domain(),
        dt = d[1] - d[0];
        if(d[0] < minT) d[0] = minT, d[1] = +d[0] + dt;
        if(d[1] > maxT) d[1] = maxT, d[0] = d[1] - dt;
        x.domain(d);
        zoom.translate([
          t[0] - Math.max(0, ext[0]),
          t[1] - Math.max(0, ext[1])
        ]);
      }
    }

Andrew Mao

unread,
Sep 21, 2012, 6:06:40 PM9/21/12
to d3...@googlegroups.com
I'm not sure what your code was doing, but I couldn't understand the math behind it and it was resulting in some weird behavior at the edges.

This is what I ended up doing (apologies since it is in coffeescript.) x-axis zoom only, hence the 0 for the second translate number.

    # Make consistent scales and zoom to enforce panning extent
    # First, check if we panned out if bounds, if so fix it
    dom = @x_scale.domain()
    dt = dom[1] - dom[0]
    if dom[0] < @lcData.start 
      dom[0] = @lcData.start
      dom[1] = dom[0] + dt
    if dom[1] > @lcData.end
      dom[1] = @lcData.end
      dom[0] = dom[1] - dt
    @x_scale.domain(dom)
 
    # Second, fix zoom translation vector for adjusted x-scale
    # This can happen from above, or from drags without zooming
    new_scale = (@lcData.end - @lcData.start) / (dom[1] - dom[0])
    @zoom_graph
      .scale( new_scale )
      .translate([ -@x_bottom(dom[0]) * new_scale , 0])


x_scale is the zooming scale. x_bottom is a static scale (what x_scale would compute if it was at zoom level 1 with no translation) that just happened to be lying around, it translates the domain of x_scale into screen pixels. I use this code to update things as well when there are other drag events that also change the zoom window.

Tom Crockett

unread,
Jun 18, 2013, 9:46:22 PM6/18/13
to d3...@googlegroups.com
FYI, here's the commit where the extent method was removed: https://github.com/mbostock/d3/commit/61d3d897cbfd0d7afa0490f432cfa68be37d3e33
Reply all
Reply to author
Forward
0 new messages