开发者

How to zoom in smoothly on a marker in Google Maps?

I'd like to be able to zoom in smoothly on a marker in Google Maps. If one just s开发者_如何学Cets the zoom on double click, the map is suddenly on that zoom level, without any smooth transition.

Zooming in only one level further than the current level, Google Maps shows a nice smooth transition. So it must be possible to zoom in smoothly for more than one level, but how?


As luck would have it, I wanted to achieve the same effect recently, and found a solution, which I made a post about. Basically, just setting a timeout for each transition isn't enough, because it could easily result in a 'start-stop' kind of zoom if Google's zoom effect isn't finished yet, or has long finished.

As Martin mentioned, there are some disadvantages to this, which I won't reiterate. Whether you use this in the end is your choice, and depends largely on your users' CPU power and/or browser. It is a nice effect though, and sure to impress some, when used wisely.

My solution was the following:

// example marker:
var marker = new google.maps.Marker({
    map: map, 
    position: new google.maps.LatLng(-20.3,30.3)
});


// add the double-click event listener
google.maps.event.addListener(marker, 'dblclick', function(event){
    map = marker.getMap();    
    map.setCenter(overlay.getPosition()); // set map center to marker position
    smoothZoom(map, 12, map.getZoom()); // call smoothZoom, parameters map, final zoomLevel, and starting zoom level
});


// the smooth zoom function
function smoothZoom (map, max, cnt) {
    if (cnt >= max) {
        return;
    }
    else {
        z = google.maps.event.addListener(map, 'zoom_changed', function(event){
            google.maps.event.removeListener(z);
            smoothZoom(map, max, cnt + 1);
        });
        setTimeout(function(){map.setZoom(cnt)}, 80); // 80ms is what I found to work well on my system -- it might not work well on all systems
    }
}  

Basically what it comes down to is adjusting the zoom level by one, listening for the zoom_changed event, waiting 80ms before adjusting the zoom-level by one again, etc. What's nice about this is that the zoom_changed event seems to be called after the smooth transition provided by Google Maps, but before the actual images are loaded, so it doesn't waste bandwidth too much.

The 80ms in the timeout is also a magic number I came up with - you would be well-advised to do a more thorough test and see what works on different systems and browsers, and perhaps change the algorithm slightly based on your findings or for different systems.

It's probably also not necessary to add and remove the listener every time, but you can make that small improvement yourself if you so wish.


This one worked for me nicely:

function animateMapZoomTo(map, targetZoom) {
    var currentZoom = arguments[2] || map.getZoom();
    if (currentZoom != targetZoom) {
        google.maps.event.addListenerOnce(map, 'zoom_changed', function (event) {
            animateMapZoomTo(map, targetZoom, currentZoom + (targetZoom > currentZoom ? 1 : -1));
        });
        setTimeout(function(){ map.setZoom(currentZoom) }, 80);
    }
}


You could try to use a setInterval to zoom in one level at a time, and clear it when you reach your desired level.

The problem with this is that the interval that will make it work is entirely dependent on the cpu and bandwidth of the users machine (how fast can it load and render the new set of image tiles).

Tbh, i'm not sure it can be done so that it will work great in every situation, but a small interval between zoom levels might help a bit.

A few things to keep in mind tho:

  1. this will put a lot more stress on the users cpu and bandwidth than going directly to the chosen zoomlevel
  2. the user will have to wait until this is done to start interacting with the map, which could easily become a very bad user experience.

Those two and probably other reasons are why google didn't build the kind of zoom you wish into maps in the first place - because it's a bad idea...


@Herman Schaaf Your solution is great but when you double click it skips a few zooms :D So I have made a solution + smoothZoom out I can't take all the credit I have edited JesseDobbelaere's code from jsfiddle.net It's a mix of yours and Jesse's code.

function smoothZoom(map, level, cnt, mode)
{
    if(mode == true)
    {
        if (cnt >= level) {
            return;
        }
        else
        {
            if((maxZoomOut + 2) <= cnt)
            {
                var z = google.maps.event.addListener(map, 'zoom_changed', function(event)
                {
                    google.maps.event.removeListener(z);
                    map.setCenter(marker.getPosition());
                    smoothZoom(map, level, cnt + 1, true);
                });
                setTimeout(function(){map.setZoom(cnt);}, timeOut);
            }
            else
            {
                map.setZoom(cnt);
                smoothZoom(map, level, cnt + 1, true);
            }
        }
    }
    else 
    {
        if (cnt < level) {
            return;
        }
        else
        {
            var z = google.maps.event.addListener(map, 'zoom_changed', function(event)
            {
                google.maps.event.removeListener(z);
                map.setCenter(marker.getPosition());
                smoothZoom(map, level, cnt - 1, false);
            });
            if(maxZoomIn - 2 <= cnt)
            {
                map.setZoom(cnt);
            }
            else
            {
                setTimeout(function(){map.setZoom(cnt);}, timeOut);
            }
        }
    }
}    

I have made few more variables like timeOut and maxZoomIn Out... you can find full code on jsfiddle http://jsfiddle.net/dexy86/9afy9/


I've tried the alternatives provided above, however, whenever I view the transition in a desktop browser or mobile app, it all occurs instantly, or at least, within a period of time which is imperceptible to the eye due to the time between each loop iteration, at least on the hardware I've trialled it on.

I used the same loop as the other methods, but altered the timeout so that the zoom occurred more quickly when the zoom level was higher, and more slowly when it was lower, so the results were more visible.

Instead of using a fixed timeout period, I set the first timeout to 0, and then subsequent timeouts to the following quick and dirty equation

((1+ currentZoom) / maxZoom) * 500;

In code I assumed the max zoom as 18, but you can use maxZoom to find out what the zoom level is for a given lat/long. My code is:

timeout = (1+map.getZoom() / 18) * 500;


There's finally an example from Google on how to do it. For a plain js implementation you'll need to include tween.js and use the following code:

/* Setup */

const cameraOptions = {
    center: { lat: /* latitude value */, lng: /* longitude value */ },
    zoom: /* zoom value */
};

const mapOptions = Object.assign(Object.assign({}, cameraOptions));

let map = new google.maps.Map(document.getElementById("map"), 
    mapOptions
);


/* Animation */

new TWEEN.Tween(cameraOptions)
    .to({ zoom: /* new zoom value */ }, 1000)
    .easing(TWEEN.Easing.Quadratic.InOut) /* https://sole.github.io/tween.js/examples/03_graphs.html */
    .onUpdate(() => {
        map.moveCamera(cameraOptions);
    })
    .start();

// Setup the animation loop.
function animate(time) {
    requestAnimationFrame(animate);
    TWEEN.update(time);
}

requestAnimationFrame(animate);

You can also, for example, animate both zoom and center at the same time by declaring the new center like this:

.to({ zoom: /* new zoom value */, center: { lat: /* new latitude value */, lng: /* new longitude value */ } }, 1000)


This will zoom smoothly on desired location. Try adding these lines:

LatLng latLng = new LatLng(lat,lng);
map.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, ZOOM_FACTOR));
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜