Skip to main content

Animate a custom marker on appearance

Add animation effects to a Marker as it displays on the map to create a pop-in & wobble effect.

Note

Markers in Mapbox GL JS are placed on the map using CSS transform properties, so you can't use transform properties to apply animation effects directly on the marker element. To work around this and enable robust animation possibilities, you can nest an 'inner' div within your marker and apply your custom CSS transforms to that element.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Animate a custom marker on appearance</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.9.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.9.1/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<style>
button {
position: absolute;
margin: 20px;
font:
bold 12px/20px 'Helvetica Neue',
Arial,
Helvetica,
sans-serif;
background-color: #3386c0;
color: #fff;
z-index: 1;
border: none;
width: 200px;
cursor: pointer;
padding: 10px 20px;
border-radius: 3px;
}

button:hover {
background-color: #4ea0da;
}

/* Style the marker div as a circle with a background image */
.marker {
background-image: url('https://docs.mapbox.com/mapbox-gl-js/assets/coffee-cup-marker.svg');
background-size: contain;
background-position-x: 50%;
background-repeat: no-repeat;
border-radius: 50%;
cursor: pointer;
transition-property: width, height;
transition-duration: 0.1s;
transition-timing-function: linear;
/* Define Marker Animation */
animation:
pop-in 0.3s ease-out forwards,
wobble 1.5s ease-in-out 0.3s forwards;
transform-origin: bottom center;
}

/* Create a css triangle and add to the bottom of the marker for the marker point */
.marker::before {
content: '';
width: 0;
height: 0;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #365874;
display: block;
position: absolute;
bottom: -5px;
left: 50%;
transform: translateX(-50%);
}

/* Keyframe animation for pop-in */
@keyframes pop-in {
0% {
width: 0px;
height: 0px;
opacity: 0;
box-shadow: none;
}
70% {
width: 55px;
height: 55px;
opacity: 1;
box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.4); /* Add a subtle shadow */
}
85% {
width: 48px;
height: 48px;
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.3); /* Shrink shadow with wobble */
}
100% {
width: 50px;
height: 50px;
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); /* Final shadow size */
}
}

/* Keyframe animation for slow wobble after pop-in */
@keyframes wobble {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(2.5deg);
}
50% {
transform: rotate(-2deg);
}
65% {
transform: rotate(1deg);
}
100% {
transform: rotate(0deg);
}
}
</style>
<div id="map"></div>
<button id="addMarker" onclick="addMarker()">Add Marker to the Map</button>

<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',
// Choose from Mapbox's core styles, or make your own style with Mapbox Studio
style: 'mapbox://styles/mapbox/streets-v12',
center: [-73.21218, 44.47579],
zoom: 11
});

// Create the marker element. To allow CSS transform properties to animate
// the marker styles, we create an inner div which will receive the animation transforms
// while GL JS continues to operate on the outer marker div apply transform/translate to correctly
// position the marker on the map
const markerEl = document.createElement('div');
const markerInner = document.createElement('div');
markerInner.className = 'marker';
markerEl.appendChild(markerInner);

// declare Marker
let marker;
let markerRendered = false;

// eslint-disable-next-line no-unused-vars
function addMarker() {
const btn = document.getElementById('addMarker');

if (!markerRendered) {
// Add marker to the map
marker = new mapboxgl.Marker(markerEl)
.setLngLat([-73.21218, 44.47579])
.addTo(map);

btn.textContent = 'Reset';
markerRendered = true;
} else {
marker.remove();
btn.textContent = 'Add Marker to the Map';
markerRendered = false;
}
}
</script>

</body>
</html>
This code snippet will not work as expected until you replace YOUR_MAPBOX_ACCESS_TOKEN with an access token from your Mapbox account.
Was this example helpful?