D3.js - how to add zoom button with the default wheelmouse zoom behavior
Asked Answered
B

1

5

So I got a worldmap with mouse zoom using the default d3.behavior.zoom() and limits to prevent the map from being dragged completely out of the page. This was a pain to get working, but it now work.

My problem now is that this project also require useless zoom + and - button in the interface and I can't found example featuring both type of zoom. It's either mouse zoom only or a crappy zoom by button only.

I tried simply calling zoom.scale(newScale); but it don't update anything. I seem to be on the right track since in the console you can see that the scale is updated but it don't update and when I zoom with the mouse it suddenly skip to the scale that was defined using the button. But it seem I also need to update the translate and I'm not sure how to get like the center of the map and calculate the translate needed to zoom to there.

I also need to know how to update the projection after calling zoom.scale(newScale); if it's the way to do that.

I made a simplified demo with zoom button obviously not working right now. http://bl.ocks.org/jfmmm/f5c62bc056e557b80447

Thanks!

edit: So close now, it zoom to the center of the map because I use the same calculation I used to calculate the middle of the screen, but whit the new scale. The problem is that I want it to zoom on the object in the middle of the screen, not always the country at the middle of the map.

function zoomBtn(action) {
    var currentZoom = zoom.scale();

    if( action == 'in' ){
        if(currentZoom < options.maxZoomLevel){
            var newScale = Math.floor(currentZoom) + 1;

            var b = path.bounds(mapFeatures);
            var t = [(width - newScale * (b[1][0] + b[0][0])) / 2, (height - newScale * (b[1][1] + b[0][1])) / 2];

            zoom.scale(newScale)
                .translate(t)
                .event(svg);
        }
    }else{
        if(currentZoom > options.minZoomLevel){
            var newScale = Math.floor(currentZoom) - 1;

            var b = path.bounds(mapFeatures);
            var t = [(width - newScale * (b[1][0] + b[0][0])) / 2, (height - newScale * (b[1][1] + b[0][1])) / 2];

            zoom.scale(newScale)
                .translate(t)
                .event(svg);
        }
    }
}

i'll update my example in a min.

Bolster answered 15/8, 2014 at 19:56 Comment(4)
You can update the projection with zoom.event(svg). See github.com/mbostock/d3/wiki/Zoom-Behavior#eventAlgid
That worked really well for the zoom thx!Bolster
now my problem is that it zoom to the top left corner of the map. How can I set it to zoom to the center? zoom.translate or zoom.center? and how do I get the center position.Bolster
mbostock now has a example of zoom buttons that focus on the center here (April 9, 2015): bl.ocks.org/mbostock/7ec977c95910dd026812Vino
S
8

The math for zooming in to the center of the screen, and not towards the top-left corner or 0lat/0lon, is fairly tricky to get right. I'm copying it from Wil Linssen's example and grafting it on to Mike Bostock's Map Pan & Zoom I. Importantly, that example uses SVG transforms to rerender the map, rather than recomputing the projection constantly. There are a few ways you might want the buttons to work:

  • Zoom Buttons I - Pressing a button causes a transition to the new view. Holding the button down does not restart the transition.

  • Zoom Buttons II - Pressing and holding the button causes the new view to be updated immediately, every 40ms. This leads to unpleasant jumps, especially when clicking the button repeatedly.

  • Zoom Buttons III - 100ms chained transitions that continue until the button is no longer held or the scaleExtent is met. Control logic prevents any undesirable instant panning. This is the one to use.

Again, getting the details right is tricky, which is why I'm providing full examples. But satisfyingly, the core logic does make sense. You're zeroing a vector, stretching it, and unzeroing it:

x = (x - center[0]) * factor + center[0];
y = (y - center[1]) * factor + center[1];
Suzerainty answered 26/11, 2014 at 21:33 Comment(7)
When I try this I get "ReferenceError: center is not defined"Plutus
Look in the examples to see how it's defined. Those last two lines are excerpts.Suzerainty
Mhh I'm now using #18489472 this one from Ilya and it's working without center. So it's fine =)Plutus
Yeah, he calls it c, I call it center.Suzerainty
Ahh okay you declared it on the very top. Ok didn't see that sorry.Plutus
To clearify the center property should be half the viewbox width and height when the viewbox is set. This is important for responsive svg's. My viewbox had width and height oh 4000 so the center is 2000 for both width and height. like so: center = [4000 / 2, 4000 / 2],Arellano
Also if you translate the svg when initializing the zoom you need to add that to the center property like so: center[0] += startWidth and center[1] += startHeight.Arellano

© 2022 - 2024 — McMap. All rights reserved.