Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 304
jfmmm Avatar asked Feb 12 '23 06:02

jfmmm


1 Answers

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];
like image 172
mgold Avatar answered Feb 15 '23 10:02

mgold