32

Using the Google Maps API v3 I've been able to update multiple positions of markers via an AJAX call. However, it lacks any transition. Code below:

if ( !latlong.equals( point.latlong ) ) {
    point.latlong = latlong;
    point.marker.setPosition(latlong);
}

The drawback is that setPosition has no native animation method. Does anyone know any methods for extending setPosition so the marker can fluently "move" from it's old to new position? Or any methods available? I have not been able to find any documentation. Thanks!

vabada
  • 1,738
  • 4
  • 29
  • 37
crockpotveggies
  • 12,682
  • 12
  • 70
  • 140

3 Answers3

59

I did not find any native way to create this animation. You can create your own animation by stepping the position from the current point to the final point using the setPosition. Here is a code snippet to give you an idea:

var map = undefined;
var marker = undefined;
var position = [43, -89];

function initialize() {

    var latlng = new google.maps.LatLng(position[0], position[1]);
    var myOptions = {
        zoom: 8,
        center: latlng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

    marker = new google.maps.Marker({
        position: latlng,
        map: map,
        title: "Your current location!"
    });

    google.maps.event.addListener(map, 'click', function(me) {
        var result = [me.latLng.lat(), me.latLng.lng()];
        transition(result);
    });
}

var numDeltas = 100;
var delay = 10; //milliseconds
var i = 0;
var deltaLat;
var deltaLng;
function transition(result){
    i = 0;
    deltaLat = (result[0] - position[0])/numDeltas;
    deltaLng = (result[1] - position[1])/numDeltas;
    moveMarker();
}

function moveMarker(){
    position[0] += deltaLat;
    position[1] += deltaLng;
    var latlng = new google.maps.LatLng(position[0], position[1]);
    marker.setPosition(latlng);
    if(i!=numDeltas){
        i++;
        setTimeout(moveMarker, delay);
    }
}

This can probably be cleaned up a bit, but will give you a good start. I am using JavaScript's setTimeout method to create the animation. The initial call to 'transition' gets the animation started. The parameter to 'transition' is a two element array [lat, lng]. The 'transition' function calculates the step sizes for lat and lng based upon a couple of animation parametes (numDeltas, delay). It then calls 'moveMarker'. The function 'moveMarker' keeps a simple counter to indicate when the marker has reached the final destination. If not there, it calls itself again.

Here is a jsFiddle of the code working: https://jsfiddle.net/rcravens/RFHKd/2363/

Hope this helps.

Bob

rcravens
  • 8,320
  • 2
  • 33
  • 26
  • 4
    Nice solution! I modified your code to be able to add a specific speed in km/h : http://jsfiddle.net/pmrotule/9tfq5sqc/8/ – pmrotule Jun 17 '15 at 01:27
  • @pmrotule Great one. I tried to modify to show dynamic labels on the marker. But may be because it constantly changes the marker, its not showing. Any idea how I can proceed? – User-8017771 Jan 23 '18 at 11:26
  • @User-8017771 Not really, I would have to see your code. I guess it hides the label when you change the position. To have the full control on the look of your marker, you can use something like this https://github.com/aaronmiler/beer-map/blob/master/app/assets/javascripts/custom_marker.js – pmrotule Jan 23 '18 at 11:39
  • @pmrotule I haven't changed much of your code.. Just for testing, I added label as the latitude in your js fiddle. Line 72 & 73: `label: startPos[0], title: startPos[0]` May be its something else that needs to be done? – User-8017771 Jan 23 '18 at 11:44
  • @pmrotule One more thing to consider is: if the label is static it gets displayed. But when I try to make it dynamic, it doesnt get displayed at all.. Any thoughts? – User-8017771 Jan 23 '18 at 12:08
  • 1
    @User-8017771 I don't see why you would set the label as the latitude... `label` is an option of `google.maps.Marker` so you can set it when the marker gets created or update it later using `.setOptions()`. For instance, in my fiddle, you could add `marker.setOptions({ label: lat.toString() });` at the end of the function `moveMarker()`. And please create a new question on Stackoverflow if you need more help. – pmrotule Jan 23 '18 at 12:37
11

In case you want smooth animations (with easing), these libraries should help:

https://github.com/terikon/marker-animate-unobtrusive

http://terikon.github.io/marker-animate-unobtrusive/demo/unobtrusive/markermove-sliding.html

Vlad Lego
  • 1,670
  • 1
  • 18
  • 19
8

I know its late but it might help the future SO wanderers.
Problem Statement: write a function(and not a library due to specific use-case) to animate a google maps marker to a new location.
Solution is based on this awesome library marker-animate-unobtrusive

function animateMarkerTo(marker, newPosition) {
    var options = {
        duration: 1000,
        easing: function (x, t, b, c, d) { // jquery animation: swing (easeOutQuad)
            return -c *(t/=d)*(t-2) + b;
        }
    };

    window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
    window.cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame;

    // save current position. prefixed to avoid name collisions. separate for lat/lng to avoid calling lat()/lng() in every frame
    marker.AT_startPosition_lat = marker.getPosition().lat();
    marker.AT_startPosition_lng = marker.getPosition().lng();
    var newPosition_lat = newPosition.lat();
    var newPosition_lng = newPosition.lng();

    // crossing the 180° meridian and going the long way around the earth?
    if (Math.abs(newPosition_lng - marker.AT_startPosition_lng) > 180) {
        if (newPosition_lng > marker.AT_startPosition_lng) {
            newPosition_lng -= 360;
        } else {
            newPosition_lng += 360;
        }
    }

    var animateStep = function(marker, startTime) {
        var ellapsedTime = (new Date()).getTime() - startTime;
        var durationRatio = ellapsedTime / options.duration; // 0 - 1
        var easingDurationRatio = options.easing(durationRatio, ellapsedTime, 0, 1, options.duration);

        if (durationRatio < 1) {
            marker.setPosition({
                lat: (
                    marker.AT_startPosition_lat +
                    (newPosition_lat - marker.AT_startPosition_lat)*easingDurationRatio
                ),
                lng: (
                    marker.AT_startPosition_lng +
                    (newPosition_lng - marker.AT_startPosition_lng)*easingDurationRatio
                )
            });

            // use requestAnimationFrame if it exists on this browser. If not, use setTimeout with ~60 fps
            if (window.requestAnimationFrame) {
                marker.AT_animationHandler = window.requestAnimationFrame(function() {animateStep(marker, startTime)});
            } else {
                marker.AT_animationHandler = setTimeout(function() {animateStep(marker, startTime)}, 17);
            }

        } else {
            marker.setPosition(newPosition);
        }
    }

    // stop possibly running animation
    if (window.cancelAnimationFrame) {
        window.cancelAnimationFrame(marker.AT_animationHandler);
    } else {
        clearTimeout(marker.AT_animationHandler);
    }

    animateStep(marker, (new Date()).getTime());
}
mohitSehgal
  • 444
  • 5
  • 10
  • Thanks for this. It actually works but in my implementation, it leaves ghost markers. How do I fix that? – Mr Smith Aug 03 '19 at 11:41
  • 1
    can you provide some more info or a fiddle maybe ? – mohitSehgal Aug 03 '19 at 12:44
  • Thanks for the swift reply. I'm using this function in an Angular app where the coordinates are coming from Firebase. The issue is that after the first few movements, the icon of the marker gets distorted and shrinks. The marker moves smoothly but leaves a trail of "Ghost" markers. It also appears at the destination before it moves there. – Mr Smith Aug 04 '19 at 01:11
  • I was able to fix the issue. It was related to how I was updating the coordinates of trucks that were already in my local state. Thanks for the function. – Mr Smith Aug 05 '19 at 08:56