Mapbox GL JS animating multiple points along separate paths, duration not consistent

I want to animate multiple points along predetermined paths on a Mapbox map. I have found this example on the Mapbox website. It animates only one point along one line: https://docs.mapbox.com/mapbox-gl-js/example/animate-point-along-route/

I also found this example here on stackoverflow: Animate multiple point along routes using Mapbox-GL.JS

With the second example (multiple points with multiple lines), I started experimenting, adding my own coordinates for routes and points. My issue is that although I can see all points "moving" along their designated paths, only the first one behaves as expected, the other two only jumps from the first coordinate to the second. I'd like all three points to behave as the first one, moving smoothly along an arc. I include the full code below. https://jsfiddle.net/szabokrissz96/dfxy0gcu/1/

<!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">
<link href="https://api.mapbox.com/mapbox-gl-js/v2.12.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v2.12.0/mapbox-gl.js"></script>
<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>
<script src="https://unpkg.com/@turf/turf@6/turf.min.js"></script>
 
<div id="map"></div>
<div class="overlay">
<button id="replay">Replay</button>
</div>
 
<script>
 mapboxgl.accessToken = 'pk.eyJ1IjoiYXRsbyIsImEiOiJjam5mdDVldmkwbTJ6M3BwNW13ZXhuYXM4In0.gikvkhPkud0GJy1FxG6-Zg';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v9',
    center: [118.0148634, -2.548926],
    zoom: 1
});
var route = {
    "type": "FeatureCollection",
    "features": [
        
        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                   [-24.716854,48.197951],[64.228455,17.102469]
                ]
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                   [-17.861384,-18.016869],[56.845644,-22.305867]
                ]
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                   [118.017516,30.565573],[11.669865,29.347228]
                ]
            }
        }
    ]
};

// 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": [-24.716854,48.197951]
            }
        },{
            "type": "Feature",
            "properties": {},
            "geometry": {
                "type": "Point",
                "coordinates": [-17.861384,-18.016869]
            }
        },{
            "type": "Feature",
            "properties": {},
            "geometry": {
                "type": "Point",
                "coordinates": [118.017516,30.565573]
            }
        }

    ]
};

// Calculate the distance in kilometers between route start/end point.
for(i=0;i<2;i++) {
   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 rate
var steps = 500;

// Draw an arc between the `origin` & `destination` of the two points
for (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 coordinates
route.features[0].geometry.coordinates = arc;

// Used to increment the value of the point measurement against the route.
var counter = 0;

map.on('load', function () {
    // 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": 1,
            "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-allow-overlap": true,
            "icon-ignore-placement": true
        }
    });

    function animate(featureIdx, cntr) {
        // Update point geometry to a new position based on counter denoting
        // the index to access the arc.
        if (cntr >= route.features[featureIdx].geometry.coordinates.length){
          return;
        }
        point.features[featureIdx].geometry.coordinates = route.features[featureIdx].geometry.coordinates[cntr];


        point.features[featureIdx].properties.bearing = turf.bearing(
            turf.point(route.features[featureIdx].geometry.coordinates[cntr >= steps ? cntr - 1 : cntr]),
            turf.point(route.features[featureIdx].geometry.coordinates[cntr >= steps ? cntr : cntr + 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 (cntr < steps) {
            requestAnimationFrame(function(){animate(featureIdx, cntr+1);});
        }

    }

    document.getElementById('replay').addEventListener('click', function() {
        // Set the coordinates of the original point back to origin
        point.features[0].geometry.coordinates = origin;

        // Update the source layer
        map.getSource('point').setData(point);

        // Reset the counter
        cntr = 0;

        // Restart the animation.
        animate(0,cntr);
        animate(1,cntr);
        animate(2,cntr)
    });

    // Start the animation.
    animate(0, 0);
    animate(1, 0);
    animate(2, 0);
});



</script>
 
</body>
</html>

Upvotes: 0

Views: 421

Answers (1)

biggigt
biggigt

Reputation: 39

I guess you have to add 3 different point markers, and 3 different routes. And animate them in one function, just step by step. Also make sure that each each route, after dividing it into segments, has same amount of points (steps for animation)

Upvotes: 0

Related Questions