Skip to content

Leaflet.js Animations — Smooth Map Motion and Transitions

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Leaflet.js Animations. We cover key concepts, practical examples, and best practices to help you master this topic.

Leaflet.js animations create smooth transitions for map movement, marker motion, and feature visualization using built-in animation methods and requestAnimationFrame.

What You'll Learn

By the end of this guide, you will animate map pan and zoom transitions, fly to coordinates with custom duration and easing, animate markers along paths, create pulsing marker effects, and animate GeoJSON features.

Animated Pan

map.panTo([40.7128, -74.0060], {
    animate: true,
    duration: 2.0
});

Fly To

map.flyTo([51.505, -0.09], 13, {
    animate: true,
    duration: 3,
    easeLinearity: 0.3
});

Animated Marker

function moveMarker(marker, targetLatLng, duration) {
    var start = marker.getLatLng();
    var startTime = performance.now();

    function animate(time) {
        var elapsed = (time - startTime) / 1000;
        var t = Math.min(elapsed / duration, 1);
        var lat = start.lat + (targetLatLng.lat - start.lat) * t;
        var lng = start.lng + (targetLatLng.lng - start.lng) * t;
        marker.setLatLng([lat, lng]);
        if (t < 1) requestAnimationFrame(animate);
    }

    requestAnimationFrame(animate);
}

Pulsing Marker

var pulsingIcon = L.divIcon({
    className: 'pulsing-marker',
    html: '<div class="pulse"></div>',
    iconSize: [30, 30]
});

L.marker([40.7128, -74.0060], { icon: pulsingIcon }).addTo(map);

// CSS:
// .pulsing-marker .pulse { width: 20px; height: 20px; border-radius: 50%; background: #ff6b35; animation: pulse 1.5s infinite; }
// @keyframes pulse { 0% { transform: scale(1); opacity: 1; } 100% { transform: scale(2); opacity: 0; } }

Common Mistakes

1. Not Using requestAnimationFrame

setInterval for animations causes jank. Use requestAnimationFrame for smooth 60fps motion.

2. Disabling Map Interaction During Animation

Set noMoveStart: true on panTo to prevent interaction during animation.

3. Long Duration Values

Duration over 5 seconds feels slow. Use 1-3 seconds for most animations.

4. Interrupting Animation

Calling panTo while an animation is running cancels the previous one. Check map._animating before starting.

5. No Easing

Linear motion looks unnatural. Leaflet uses easeInOut by default. Set easeLinearity for custom easing.

Practice Questions

Q1: What method animates the map to a new center? A: map.panTo(latlng, { animate: true, duration: seconds }).

Q2: What is the difference between panTo and flyTo? A: flyTo animates both center and zoom simultaneously with a dramatic flight effect.

Q3: How do you animate a marker along a path? A: Use requestAnimationFrame and update marker.setLatLng each frame with interpolated coordinates.

Q4: How do you create a pulsing marker? A: Use L.divIcon with CSS keyframe animations on the icon element.

Q5: How do you cancel an animation? A: Leaflet panTo animation auto-cancels on new map interaction. Custom animations need a cancel flag.

Challenge: Build a map tour that flies to 5 major world cities sequentially with a 2-second delay between each. Add a progress indicator.

FAQ

Does Leaflet support CSS transitions?

Not on map layers. Use requestAnimationFrame for custom layer animations.

How do I animate GeoJSON layer opacity?

Set layer.setStyle({ opacity: 0 }) then transition with step function.

Can I animate tile layer opacity?

Yes. Set tileLayer.setOpacity(start) then transition to target using setInterval.

Is flyTo available in all versions?

flyTo was added in Leaflet 1.0. Earlier versions use panTo.

How do I animate multiple markers?

Use a batch animation function that manages multiple requestAnimationFrame loops.

Try It Yourself

Build a map with a moving marker.

<!DOCTYPE html>
<html>
<head>
    <title>Animation Demo</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.9/dist/leaflet.js"></script>
    <style>
        .moving-marker { background: #ff6b35; border: 3px solid #fff; border-radius: 50%; width: 16px; height: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); }
    </style>
</head>
<body style="font-family:sans-serif;padding:20px;">
<h2>Animated Marker</h2>
<button onclick="flyToNYC()">Fly to NYC</button>
<button onclick="animateMarker()">Move Marker</button>
<div id="map" style="height:400px;border-radius:8px;margin-top:8px;"></div>
<script>
var map = L.map('map').setView([40.7128, -74.0060], 12);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; OSM'
}).addTo(map);

var marker = L.marker([40.7128, -74.0060], {
    icon: L.divIcon({ className: 'moving-marker', iconSize: [16, 16] })
}).addTo(map);

function flyToNYC() {
    map.flyTo([40.7128, -74.0060], 14, {
        duration: 2,
        easeLinearity: 0.5
    });
}

function animateMarker() {
    var target = L.latLng(
        40.7128 + (Math.random() - 0.5) * 0.05,
        -74.0060 + (Math.random() - 0.5) * 0.05
    );
    var start = marker.getLatLng();
    var startTime = performance.now();
    var duration = 2;

    function step(time) {
        var t = Math.min((time - startTime) / (duration * 1000), 1);
        var lat = start.lat + (target.lat - start.lat) * t;
        var lng = start.lng + (target.lng - start.lng) * t;
        marker.setLatLng([lat, lng]);
        if (t < 1) requestAnimationFrame(step);
    }

    requestAnimationFrame(step);
}
</script>
</body>
</html>

What's Next

Build a complete map project.

Project — Complete map project.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro