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>
Then initialize Leaflet in our script.js
file:
const leafletMap = L.map(document.getElementById("leaflet-container")).setView([0, 0], 1);
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
});
};
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);
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);
}
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]);
}
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]);
}
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);
And update the coordinates just like we did for the marker:
marker.setLatLng(dataset[closestPointIndex]);
path.setLatLngs(dataset.slice(0, closestPointIndex + 1));
Here's the result:
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);
}
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:
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>
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)
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 😎Thanks for the tip! it's updated
nice solution! I struggle with existing plugins that are outdated, it will help me fore sure!