Animate a Point
Move a point continuously on the map using requestAnimationFrame for smooth, frame-based animation.
How It Works
Use requestAnimationFrame to call a function on every browser repaint (~60fps). On each frame, compute the new position and call setData() to move the point.
Key Pattern
javascript
// 1. Add point source
map.addSource('point', {
type: 'geojson',
data: {
type: 'Feature',
geometry: { type: 'Point', coordinates: [0, 0] },
properties: {}
}
});
// 2. Display as circle layer
map.addLayer({
id: 'point',
type: 'circle',
source: 'point',
paint: { 'circle-radius': 12, 'circle-color': '#f59e0b' }
});
// 3. Animate position on each frame
let t = 0;
function animate() {
t += 0.01;
const lng = Math.cos(t) * 60;
const lat = Math.sin(t) * 30;
map.getSource('point').setData({
type: 'Feature',
geometry: { type: 'Point', coordinates: [lng, lat] },
properties: {}
});
requestAnimationFrame(animate);
}
animate();Stop and Resume Animation
javascript
let animId = null;
let running = false;
function start() {
if (running) return;
running = true;
function tick() {
if (!running) return;
// update position...
animId = requestAnimationFrame(tick);
}
tick();
}
function stop() {
running = false;
if (animId) cancelAnimationFrame(animId);
}Complete Example
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link href="https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.css" rel="stylesheet" />
<script src="https://cdn.mapmetrics-atlas.net/versions/latest/mapmetrics-gl.js"></script>
<style>#map { height: 500px; width: 100%; }</style>
</head>
<body>
<div id="map"></div>
<script>
const map = new mapmetricsgl.Map({
container: 'map',
style: '<StyleFile_URL_with_Token>',
center: [0, 0],
zoom: 1.5
});
map.on('load', () => {
map.addSource('point', {
type: 'geojson',
data: { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} }
});
map.addLayer({
id: 'point',
type: 'circle',
source: 'point',
paint: { 'circle-radius': 12, 'circle-color': '#f59e0b', 'circle-stroke-width': 3, 'circle-stroke-color': '#fff' }
});
let t = 0;
function animate() {
t += 0.015;
map.getSource('point').setData({
type: 'Feature',
geometry: { type: 'Point', coordinates: [Math.cos(t) * 60, Math.sin(t * 0.7) * 30] },
properties: {}
});
requestAnimationFrame(animate);
}
animate();
});
</script>
</body>
</html>jsx
import React, { useEffect, useRef } from 'react';
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const AnimatePoint = () => {
const mapContainer = useRef(null);
const map = useRef(null);
const animId = useRef(null);
useEffect(() => {
if (map.current) return;
map.current = new mapmetricsgl.Map({
container: mapContainer.current,
style: '<StyleFile_URL_with_Token>',
center: [0, 0],
zoom: 1.5
});
map.current.on('load', () => {
map.current.addSource('point', {
type: 'geojson',
data: { type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} }
});
map.current.addLayer({
id: 'point',
type: 'circle',
source: 'point',
paint: { 'circle-radius': 12, 'circle-color': '#f59e0b', 'circle-stroke-width': 3, 'circle-stroke-color': '#fff' }
});
let t = 0;
const animate = () => {
t += 0.015;
map.current?.getSource('point')?.setData({
type: 'Feature',
geometry: { type: 'Point', coordinates: [Math.cos(t) * 60, Math.sin(t * 0.7) * 30] },
properties: {}
});
animId.current = requestAnimationFrame(animate);
};
animate();
});
return () => {
if (animId.current) cancelAnimationFrame(animId.current);
map.current?.remove();
map.current = null;
};
}, []);
return <div ref={mapContainer} style={{ height: '500px', width: '100%' }} />;
};
export default AnimatePoint;For more information, visit the MapMetrics GitHub repository.