I\'ve been looking for a good solution to animate a marker using the Google Maps JavaScript API V3. The marker will be animated on a predetermined path defined using latitud
One approach is to generate a series of locations along the route (for example 500 distinct locations) and then call marker.setPosition(nextLocation)
every X milliseconds using, for example, setTimeout.
Alternatively, you can use a symbol on your polyline, setting the percentage along the path, again in a setTimeout approach. This has the advantage you don't need to compute the locations, you can just slowly move it some tiny percentage along each X milliseconds.
Yes. it is possible to animate a marker along a route from the DirectionsService in v3.
This example uses a version of Mike Williams' epoly library ported to v3.
proof of concept fiddle
code snippet:
var map;
var directionDisplay;
var directionsService;
var stepDisplay;
var markerArray = [];
var position;
var marker = null;
var polyline = null;
var poly2 = null;
var speed = 0.000005,
wait = 1;
var infowindow = null;
var myPano;
var panoClient;
var nextPanoId;
var timerHandle = null;
function createMarker(latlng, label, html) {
// alert("createMarker("+latlng+","+label+","+html+","+color+")");
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: label,
zIndex: Math.round(latlng.lat() * -100000) << 5
});
marker.myname = label;
// gmarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
return marker;
}
function initialize() {
infowindow = new google.maps.InfoWindow({
size: new google.maps.Size(150, 50)
});
// Instantiate a directions service.
directionsService = new google.maps.DirectionsService();
// Create a map and center it on Manhattan.
var myOptions = {
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
address = 'new york'
geocoder = new google.maps.Geocoder();
geocoder.geocode({
'address': address
}, function(results, status) {
map.setCenter(results[0].geometry.location);
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
}
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
// Instantiate an info window to hold step text.
stepDisplay = new google.maps.InfoWindow();
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
}
var steps = []
function calcRoute() {
if (timerHandle) {
clearTimeout(timerHandle);
}
if (marker) {
marker.setMap(null);
}
polyline.setMap(null);
poly2.setMap(null);
directionsDisplay.setMap(null);
polyline = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
poly2 = new google.maps.Polyline({
path: [],
strokeColor: '#FF0000',
strokeWeight: 3
});
// Create a renderer for directions and bind it to the map.
var rendererOptions = {
map: map
}
directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
var start = document.getElementById("start").value;
var end = document.getElementById("end").value;
var travelMode = google.maps.DirectionsTravelMode.DRIVING
var request = {
origin: start,
destination: end,
travelMode: travelMode
};
// Route the directions and pass the response to a
// function to create markers for each step.
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
var bounds = new google.maps.LatLngBounds();
var route = response.routes[0];
startLocation = new Object();
endLocation = new Object();
// For each route, display summary information.
var path = response.routes[0].overview_path;
var legs = response.routes[0].legs;
for (i = 0; i < legs.length; i++) {
if (i == 0) {
startLocation.latlng = legs[i].start_location;
startLocation.address = legs[i].start_address;
// marker = google.maps.Marker({map:map,position: startLocation.latlng});
marker = createMarker(legs[i].start_location, "start", legs[i].start_address, "green");
}
endLocation.latlng = legs[i].end_location;
endLocation.address = legs[i].end_address;
var steps = legs[i].steps;
for (j = 0; j < steps.length; j++) {
var nextSegment = steps[j].path;
for (k = 0; k < nextSegment.length; k++) {
polyline.getPath().push(nextSegment[k]);
bounds.extend(nextSegment[k]);
}
}
}
polyline.setMap(map);
map.fitBounds(bounds);
// createMarker(endLocation.latlng,"end",endLocation.address,"red");
map.setZoom(18);
startAnimation();
}
});
}
var step = 50; // 5; // metres
var tick = 100; // milliseconds
var eol;
var k = 0;
var stepnum = 0;
var speed = "";
var lastVertex = 1;
//=============== animation functions ======================
function updatePoly(d) {
// Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
if (poly2.getPath().getLength() > 20) {
poly2 = new google.maps.Polyline([polyline.getPath().getAt(lastVertex - 1)]);
// map.addOverlay(poly2)
}
if (polyline.GetIndexAtDistance(d) < lastVertex + 2) {
if (poly2.getPath().getLength() > 1) {
poly2.getPath().removeAt(poly2.getPath().getLength() - 1)
}
poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.GetPointAtDistance(d));
} else {
poly2.getPath().insertAt(poly2.getPath().getLength(), endLocation.latlng);
}
}
function animate(d) {
// alert("animate("+d+")");
if (d > eol) {
map.panTo(endLocation.latlng);
marker.setPosition(endLocation.latlng);
return;
}
var p = polyline.GetPointAtDistance(d);
map.panTo(p);
marker.setPosition(p);
updatePoly(d);
timerHandle = setTimeout("animate(" + (d + step) + ")", tick);
}
function startAnimation() {
eol = google.maps.geometry.spherical.computeLength(polyline.getPath());
map.setCenter(polyline.getPath().getAt(0));
// map.addOverlay(new google.maps.Marker(polyline.getAt(0),G_START_ICON));
// map.addOverlay(new GMarker(polyline.getVertex(polyline.getVertexCount()-1),G_END_ICON));
// marker = new google.maps.Marker({location:polyline.getPath().getAt(0)} /* ,{icon:car} */);
// map.addOverlay(marker);
poly2 = new google.maps.Polyline({
path: [polyline.getPath().getAt(0)],
strokeColor: "#0000FF",
strokeWeight: 10
});
// map.addOverlay(poly2);
setTimeout("animate(50)", 2000); // Allow time for the initial map display
}
//=============== ~animation funcitons =====================
google.maps.event.addDomListener(window, "load", initialize);
/*********************************************************************\
* *
* epolys.js by Mike Williams *
* updated to API v3 by Larry Ross *
* *
* A Google Maps API Extension *
* *
* Adds various Methods to google.maps.Polygon and google.maps.Polyline *
* *
* .Contains(latlng) returns true is the poly contains the specified *
* GLatLng *
* *
* .Area() returns the approximate area of a poly that is *
* not self-intersecting *
* *
* .Distance() returns the length of the poly path *
* *
* .Bounds() returns a GLatLngBounds that bounds the poly *
* *
* .GetPointAtDistance() returns a GLatLng at the specified distance *
* along the path. *
* The distance is specified in metres *
* Reurns null if the path is shorter than that *
* *
* .GetPointsAtDistance() returns an array of GLatLngs at the *
* specified interval along the path. *
* The distance is specified in metres *
* *
* .GetIndexAtDistance() returns the vertex number at the specified *
* distance along the path. *
* The distance is specified in metres *
* Returns null if the path is shorter than that *
* *
* .Bearing(v1?,v2?) returns the bearing between two vertices *
* if v1 is null, returns bearing from first to last *
* if v2 is null, returns bearing from v1 to next *
* *
* *
***********************************************************************
* *
* This Javascript is provided by Mike Williams *
* Blackpool Community Church Javascript Team *
* http://www.blackpoolchurch.org/ *
* http://econym.org.uk/gmap/ *
* *
* This work is licenced under a Creative Commons Licence *
* http://creativecommons.org/licenses/by/2.0/uk/ *
* *
***********************************************************************
* *
* Version 1.1 6-Jun-2007 *
* Version 1.2 1-Jul-2007 - fix: Bounds was omitting vertex zero *
* add: Bearing *
* Version 1.3 28-Nov-2008 add: GetPointsAtDistance() *
* Version 1.4 12-Jan-2009 fix: GetPointsAtDistance() *
* Version 3.0 11-Aug-2010 update to v3 *
* *
\*********************************************************************/
google.maps.LatLng.prototype.latRadians = function() {
return this.lat() * Math.PI / 180;
}
google.maps.LatLng.prototype.lngRadians = function() {
return this.lng() * Math.PI / 180;
}
// === A method which returns a GLatLng of a point a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polyline.prototype.GetPointAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
if (this.getPath().getLength() < 2) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
var p1 = this.getPath().getAt(i - 2);
var p2 = this.getPath().getAt(i - 1);
var m = (metres - olddist) / (dist - olddist);
return new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m);
}
// === A method which returns the Vertex number at a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polyline.prototype.GetIndexAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
return i;
}
html {
height: 100%;
}
body {
height: 100%;
margin: 0px;
font-family: Helvetica, Arial;
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<div id="tools">
start:
<input type="text" name="start" id="start" value="union square, NY" />end:
<input type="text" name="end" id="end" value="times square, NY" />
<input type="submit" onclick="calcRoute();" />
</div>
<div id="map_canvas" style="width:100%;height:100%;"></div>