Animate a point along a route
Use Turf to smoothly animate a point along the distance of a line.
Note
If you want to test this example, edit it in JSFiddle or CodePen and replace the "YOUR-OWN-API-KEY" and "YOUR-OWN-VPM-ID" placeholders with your actual api key and vpm id.<!DOCTYPE html><html><head><meta charset="utf-8" /><title>Animate a point along a route</title><meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" /><script src="https://unpkg.com/unl-map-js@0.1.5/lib/unl-map-js.js"></script><link href="https://unpkg.com/unl-map-js@0.1.5/lib/unl-map-js.css" rel="stylesheet" /><linkhref="https://unpkg.com/maplibre-gl@2.1.9/dist/maplibre-gl.css"rel="stylesheet"/><style> body { margin: 0; padding: 0; } #map { position: absolute; top: 0; bottom: 0; width: 100%; }</style></head><body><style>.overlay {position: absolute;top: 10px;left: 10px;} .overlay button {font: 600 12px/20px "Helvetica Neue", Arial, Helvetica, sans-serif;background-color: #3386c0;color: #fff;display: inline-block;margin: 0;padding: 10px 20px;border: none;cursor: pointer;border-radius: 3px;} .overlay button:hover {background-color: #4ea0da;}</style><scriptsrc="https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v2.0.0/turf.min.js"charset="utf-8"></script> <div id="map"></div><div class="overlay"><button id="replay">Replay</button></div> <script>var map = new UnlSdk.Map({container: "map",apiKey: "YOUR-OWN-API-KEY",vpmId: "YOUR-OWN-VPM-ID",center: [-96, 37.8],zoom: 3,}); // San Franciscovar origin = [-122.414, 37.776]; // Washington DCvar destination = [-77.032, 38.913]; // A simple line from origin to destination.var route = {type: "FeatureCollection",features: [{type: "Feature",geometry: {type: "LineString",coordinates: [origin, destination],},},],}; // A single point that animates along the route.// Coordinates are initially set to origin.var point = {type: "FeatureCollection",features: [{type: "Feature",properties: {},geometry: {type: "Point",coordinates: origin,},},],}; // Calculate the distance in kilometers between route start/end point.var lineDistance = turf.lineDistance(route.features[0], "kilometers"); var arc = []; // Number of steps to use in the arc and animation, more steps means// a smoother arc and animation, but too many steps will result in a// low frame ratevar steps = 500; // Draw an arc between the `origin` & `destination` of the two pointsfor (var i = 0; i < lineDistance; i += lineDistance / steps) {var segment = turf.along(route.features[0], i, "kilometers");arc.push(segment.geometry.coordinates);} // Update the route with calculated arc coordinatesroute.features[0].geometry.coordinates = arc; // Used to increment the value of the point measurement against the route.var counter = 0; map.on("load", function () {map.loadImage("https://github.com/u-n-l/unl-map-js-docs/assets/airtport_15",function (error, image) {if (error) throw error;map.addImage("airport_16", image); // Add a source and layer displaying a point which will be animated in a circle.map.addSource("route", {type: "geojson",data: route,}); map.addSource("point", {type: "geojson",data: point,}); map.addLayer({id: "route",source: "route",type: "line",paint: {"line-width": 2,"line-color": "#007cbf",},}); map.addLayer({id: "point",source: "point",type: "symbol",layout: {"icon-image": "airport_15","icon-rotate": ["get", "bearing"],"icon-rotation-alignment": "map","icon-overlap": "always","icon-ignore-placement": true,},}); function animate() {// Update point geometry to a new position based on counter denoting// the index to access the arc.point.features[0].geometry.coordinates =route.features[0].geometry.coordinates[counter]; // Calculate the bearing to ensure the icon is rotated to match the route arc// The bearing is calculate between the current point and the next point, except// at the end of the arc use the previous point and the current pointpoint.features[0].properties.bearing = turf.bearing(turf.point(route.features[0].geometry.coordinates[counter >= steps ? counter - 1 : counter]),turf.point(route.features[0].geometry.coordinates[counter >= steps ? counter : counter + 1])); // Update the source with this new data.map.getSource("point").setData(point); // Request the next frame of animation so long the end has not been reached.if (counter < steps) {requestAnimationFrame(animate);} counter = counter + 1;} document.getElementById("replay").addEventListener("click", function () {// Set the coordinates of the original point back to originpoint.features[0].geometry.coordinates = origin; // Update the source layermap.getSource("point").setData(point); // Reset the countercounter = 0; // Restart the animation.animate(counter);}); // Start the animation.animate(counter);});});</script> </body></html>