Google Maps Path Animation

Animate a curved path/route between two geo-localized points markers using the Google map js v3 API. Here is the relevant part of the path animation. Modify based on your requirements. Hope it helps.


// ... init the google maps as usual then:

maps.setTwinMarker(
   { id: "a", lat: 34.0380, lng: -118.3086, bubble: 'a div defining your marker' },
   { id: "b", lat: 34.0224, lng: -118.4871, bubble: 'a div defining your marker' }
);

// ...

setTwinMarker = function( ma, mb )
{
    this.clear();
    this.addMarker( ma.lat, ma.lng, ma.id, ma.bubble, true );
    this.addMarker( mb.lat, mb.lng, mb.id, mb.bubble, true );
    this.fit();

    function larp( a, b, t )
    {
        return { lat: a.lat + ( b.lat - a.lat ) * t, lng: a.lng + ( b.lng - a.lng ) * t };
    }

    function getBezier( points, t )
    {
        var ab = larp( points[ 0 ], points[ 1 ], t );
        var bc = larp( points[ 1 ], points[ 2 ], t );
        var cd = larp( points[ 2 ], points[ 3 ], t );
        var abbc = larp( ab, bc, t );
        var bccd = larp( bc, cd, t );

        return larp( abbc, bccd, t );
    }

    function getBezierHandles( p0, p1 )
    {
        // a, b points are perpendicular to q1, q3 which are on line connecting p0 and p1.
        // slope between two points : ( y2 - y1 ) / (x2 - x1 ).
        // to get perpendicular slope use, reciprocal slope function: -(x2 - x1 ) / ( y2 - y1 )
        // curvature is distance between q1 and a; q3 and b;
        //
        //        a              b
        //        |              |
        //        |              |
        // p0-----q1-----q2-----q3-----p1

        var q2 = { lat: (p0.lat + p1.lat) / 2, lng: (p0.lng + p1.lng) / 2 };
        var q1 = { lat: (p0.lat + q2.lat) / 2, lng: (p0.lng + q2.lng) / 2 };
        var q3 = { lat: (q2.lat + p1.lat) / 2, lng: (q2.lng + p1.lng) / 2 };

        var dLat = p1.lat - p0.lat;
        var dLng = p1.lng - p0.lng;

        var curvature = 0.15;

        if( p0.lng > p1.lng )
        {
            return {
                a: { lat: q1.lat + ( -dLng * curvature ), lng: q1.lng + ( dLat * curvature ) },
                b: { lat: q3.lat + ( -dLng * curvature ), lng: q3.lng + ( dLat * curvature ) }
            };
        }
        else
        {
            return {
                a: { lat: q1.lat - ( -dLng * curvature ), lng: q1.lng + ( dLat * curvature ) },
                b: { lat: q3.lat - ( -dLng * curvature ), lng: q3.lng + ( dLat * curvature ) }
            };
        }
    }

    function getLatLngPath( p0, p1 )
    {
        var handles = getBezierHandles( p0, p1 );

        var points = [ p0, handles.a, handles.b, p1 ];

        var path = [];

        var sections = 40;

        for( var t = 0; t <= sections; ++t )
        {
            var b = getBezier( points, t / sections );

            path.push( new google.maps.LatLng( b.lat, b.lng ) );
        }

        return path;
    }

    var path = getLatLngPath( { lat: ma.lat, lng: ma.lng }, { lat: mb.lat, lng: mb.lng } );

    var dotIcon = {
        path: 'M -1,1 0,0 1,1',
        scale: 5,
        strokeWeight: 1.5,
        strokeColor: '#232e40'
    };

    _polyDot = new google.maps.Polyline( {
        path: path,
        map: _map,
        geodesic: false,
        strokeOpacity: 1,
        strokeWeight: 0,
        strokeColor: 'transparent',
        icons: [ { icon: dotIcon, repeat: '80px' } ]
    } );

    this.animate();
};

// ...

animate = function()
{
    var count = 0;

    _polyDotAnimTimerId = window.setInterval( function()
    {
        count = ++count % 80;

        if( _polyDot )
        {
            var icons = _polyDot.get( 'icons' );

            icons[ 0 ].offset = count + 'px';

            _polyDot.set( 'icons', icons );
        }

    }, 40 );
};

// ...
                    

.bubble {
    background:#232e40;
    border-radius:50%;
    height:20px;
    width:20px;
    position:relative;
    bottom:-10px;
}