DEV Community

Cover image for Animated traveling map with Leaflet
Benjamin Caure
Benjamin Caure

Posted on

Animated traveling map with Leaflet

In this tutorial, we'll learn how to animate a marker with Leaflet, without other 3rd party library or any Leaflet plugin.

Leaflet is the most famous open-source map library, with lots of plugins. 2 of them are used to animate a marker on the map:

Unfortunately, they're not maintained anymore and have lots of issue to work with the recent Leaflet versions and the modern Javascript frameworks.

However, they're not essentials to animate our own markers or tracks. Here's how to do it.

Project Initialization

Let's create a Leaflet container in our index.html file:



<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
  </head>

  <body>
    <div id="leaflet-container" style="width: 70vw; height: 70vh; margin: 50px auto"></div>
  </body>
  <script src="script.js"></script>
</html>


Enter fullscreen mode Exit fullscreen mode

Then initialize Leaflet in our script.js file:



const leafletMap = L.map(document.getElementById("leaflet-container")).setView([0, 0], 1);


Enter fullscreen mode Exit fullscreen mode

Here's our dataset: an array of random coordinates with timestamps, generated with a loop:



const dataset = [{
  lng: -100,
  lat: -80,
  timestamp: new Date().getTime()
}];
let timestamp = 1710067901850;

for (let i = 1; i < 300; i++) {
  dataset.push({
    lng: dataset[i - 1].lng + Math.random(),
    lat: dataset[i - 1].lat + Math.random(),
    timestamp: dataset[i - 1].timestamp + 500
  });
};


Enter fullscreen mode Exit fullscreen mode

Finally, we'll create the marker from the origin position and add it to the map:



let marker = L.marker(dataset[0], { icon: new L.Icon({ iconUrl: '/marker.svg', iconSize: [20, 20], iconAnchor: [10, 20] }) });
marker.addTo(leafletMap);


Enter fullscreen mode Exit fullscreen mode

Animate The Marker

Now we'll create the start() function which use an interval to tick the clock every second.

The tick function will move the marker position during the period between the first and the last timestamp:



let tick = dataset[0].timestamp;
const endTime = dataset[dataset.length - 1].timestamp;
let interval;
function start() {
  interval = setInterval(() => {
    if (tick > endTime) {
      // End of animation: leave tracks and markers where they are but stop intervals
      stop();
    } else {
      // Create or update the marker
      moveMarker();

      // Update tick including speed
      tick += 500;
    }
  }, 100);
}


Enter fullscreen mode Exit fullscreen mode

The stop() function removes the interval, moves the marker back to its origin, and reset the tick:



function stop() {
  clearInterval(interval);
  tick = dataset[0].timestamp;
  marker.setLatLng(dataset[0]);
}


Enter fullscreen mode Exit fullscreen mode

Let's create our moveMarker() function: first it loops on our dataset to find the timestamp closest to our tick. Then it moves the marker by updating the marker's coordinates «latlng» property:



function moveMarker() {
  // Look for the timestamp just before tick
  let closestPointIndex = -1;
  for (let i = 0; i < dataset.length && closestPointIndex < 0; i += 1) {
    const timestamp = dataset[i].timestamp;
    if (timestamp > tick) {
      closestPointIndex = i === 0 ? 0 : i - 1;
    }
  }
  marker.setLatLng(dataset[closestPointIndex]);
}


Enter fullscreen mode Exit fullscreen mode

Draw the path

Let's improve our animation by drawing also the track. First, we create a polyline just after our marker:



let path = L.polyline(dataset[0]);
path.addTo(leafletMap);


Enter fullscreen mode Exit fullscreen mode

And update the coordinates just like we did for the marker:



  marker.setLatLng(dataset[closestPointIndex]);
  path.setLatLngs(dataset.slice(0, closestPointIndex + 1));


Enter fullscreen mode Exit fullscreen mode

Here's the result:

a marker in motion and its path is drawn

Add playback controls

We already have the start() and stop() functions.
Now let's add some controls functions, like pause(): it's simple as it's the same than stop() but doesn't reset the tick:



function pause() {
  clearInterval(interval);
}


Enter fullscreen mode Exit fullscreen mode

Like this, the start() function will continue from where it was paused.

We can add our playback buttons, which are bound to their corresponding functions:

play stop and pause buttons

Here's the HTML code:



<style>
button {
background-color: slateblue;
border: 1px solid skyblue;
color: lightblue;
font-size: 1.25em;
padding: 10px 20px;
width: 70px;
display: flex;
align-items: center;
justify-content: center;
}
button:hover {
background-color: darkslateblue;
color: white;
}
</style>

<body>
<section style="display: flex; width: 200px; margin: auto">
<button
onclick="start()"
style="border-radius: 10px 0 0 10px; font-size: 1.5em"
>

</button>
<button
onclick="pause()"
style="border-radius: 0"
>
❚❚
</button>
<button
onclick="stop()"
style="border-radius: 0 10px 10px 0; font-size: 2em; padding-top: 2px"
>

</button>

</section>

Enter fullscreen mode Exit fullscreen mode




Conclusion

In this article, we've coded an animated traveling map with Leaflet.
This solution is really simple and doesn't require third-party plugins.

It's also scalable as it was tester with 20 simultaneous travels.

You can find the working website in this stackblitz.

Top comments (3)

Collapse
 
thomasbnt profile image
Thomas Bnt ☕

Hello cool post !
Don't hesitate to put colors on your codeblock like this example for have to have a better understanding of your code 😎

console.log('Hello world!');
Enter fullscreen mode Exit fullscreen mode

Example of how to add colors and syntax in codeblocks

Collapse
 
bcaure profile image
Benjamin Caure

Thanks for the tip! it's updated

Collapse
 
benkarrington profile image
b.karrington

nice solution! I struggle with existing plugins that are outdated, it will help me fore sure!