Animate the camera along a path
Use the FreeCamera
API to follow a path over 3D terrain.
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Animate the camera along a path</title><meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"><link href="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.css" rel="stylesheet"><script src="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.js"></script><style>body { margin: 0; padding: 0; }#map { position: absolute; top: 0; bottom: 0; width: 100%; }</style></head><body><div id="map"></div> <script src="https://docs.mapbox.com/mapbox-gl-js/assets/routes.js"></script><script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script><script> // TO MAKE THE MAP APPEAR YOU MUST // ADD YOUR ACCESS TOKEN FROM // https://account.mapbox.com mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';const map = new mapboxgl.Map({container: 'map',zoom: 11.53,center: [6.5615, 46.0598],pitch: 65,bearing: -180,// Choose from Mapbox's core styles, or make your own style with Mapbox Studiostyle: 'mapbox://styles/mapbox/outdoors-v12',interactive: false}); // `routes` comes from https://docs.mapbox.com/mapbox-gl-js/assets/routes.js,// which has properties that are in the shape of an array of arrays that correspond// to the `coordinates` property of a GeoJSON linestring, for example:// [// [6.56158, 46.05989],// [6.56913, 46.05679],// ...// ]// this is the path the camera will look atconst targetRoute = routes.target;// this is the path the camera will move alongconst cameraRoute = routes.camera; // add terrain, sky, and line layers once the style has loadedmap.on('style.load', () => {map.addSource('mapbox-dem', {'type': 'raster-dem','url': 'mapbox://mapbox.mapbox-terrain-dem-v1','tileSize': 512,'maxzoom': 14});map.setTerrain({ 'source': 'mapbox-dem', 'exaggeration': 1.5 });map.addSource('trace', {type: 'geojson',data: {'type': 'Feature','properties': {},'geometry': {'type': 'LineString','coordinates': targetRoute}}});map.addLayer({type: 'line',source: 'trace',id: 'line',paint: {'line-color': 'black','line-width': 5},layout: {'line-cap': 'round','line-join': 'round'}});}); // wait for the terrain and sky to load before starting animationmap.on('load', () => {const animationDuration = 80000;const cameraAltitude = 4000;// get the overall distance of each route so we can interpolate along themconst routeDistance = turf.lineDistance(turf.lineString(targetRoute));const cameraRouteDistance = turf.lineDistance(turf.lineString(cameraRoute)); let start; function frame(time) {if (!start) start = time;// phase determines how far through the animation we areconst phase = (time - start) / animationDuration; // phase is normalized between 0 and 1// when the animation is finished, reset start to loop the animationif (phase > 1) {// wait 1.5 seconds before loopingsetTimeout(() => {start = 0.0;}, 1500);} // use the phase to get a point that is the appropriate distance along the route// this approach syncs the camera and route positions ensuring they move// at roughly equal rates even if they don't contain the same number of pointsconst alongRoute = turf.along(turf.lineString(targetRoute),routeDistance * phase).geometry.coordinates; const alongCamera = turf.along(turf.lineString(cameraRoute),cameraRouteDistance * phase).geometry.coordinates; const camera = map.getFreeCameraOptions(); // set the position and altitude of the cameracamera.position = mapboxgl.MercatorCoordinate.fromLngLat({lng: alongCamera[0],lat: alongCamera[1]},cameraAltitude); // tell the camera to look at a point along the routecamera.lookAtPoint({lng: alongRoute[0],lat: alongRoute[1]}); map.setFreeCameraOptions(camera); window.requestAnimationFrame(frame);} window.requestAnimationFrame(frame);});</script> </body></html>