Animate a line
Replicates the MapLibre example Animate a line. The line layer and its styling are YAML. The animation appends a vertex every frame and hands the whole line to <ml-map>’s updateLayerData() — the element finds the layer’s source itself, so there is no getSource(...).setData() bookkeeping. The Pause button rides a corner slot.
The YAML half
Section titled “The YAML half”# yaml-language-server: $schema=https://docs.maplibre-yaml.org/schema/latest/map.schema.json# Gallery twin of https://maplibre.org/maplibre-gl-js/docs/examples/animate-a-line/# The YAML half: the line layer and its styling, starting as a stub. The# animation loop grows it through <ml-map>'s updateLayerData() — see# animate-a-line.js.type: mapid: animate-a-lineconfig: center: [0, 0] zoom: 0.5 mapStyle: "https://demotiles.maplibre.org/style.json"layers: - id: line-animation type: line source: type: geojson route: coordinates: [[0, 0], [0.5, 0.2]] layout: line-cap: round line-join: round paint: line-color: "#ed6498" line-width: 5 line-opacity: 0.8The page half
Section titled “The page half”This markup goes inside <ml-map>; children with a slot ride its corner system.
<div slot="top-left" class="gallery-panel"> <button type="button" data-pause>Pause</button></div>The JavaScript half
Section titled “The JavaScript half”// The JavaScript half: a requestAnimationFrame loop feeding <ml-map>'s// updateLayerData(). Each frame appends a vertex of a sine wave and hands// the whole line to the layer — no getSource(...).setData bookkeeping.const mapEl = document.querySelector("ml-map");const pauseButton = document.querySelector("[data-pause]");
const speedFactor = 30; // frames per degree of longitudelet coordinates = [[0, 0]];let startTime = 0;let progress = 0;let resumed = true; // restart the clock from `progress` on the next framelet frame = 0;
function animateLine(timestamp) { if (resumed) { startTime = timestamp - progress; resumed = false; } progress = timestamp - startTime;
if (progress > speedFactor * 360) { // Finished a loop: start over. startTime = timestamp; progress = 0; coordinates = [[0, 0]]; } else { const x = progress / speedFactor; const y = Math.sin((x * Math.PI) / 90) * 40; coordinates.push([x, y]); mapEl.updateLayerData("line-animation", { type: "Feature", properties: {}, geometry: { type: "LineString", coordinates }, }); } frame = requestAnimationFrame(animateLine);}
mapEl.mapReady().then(() => { frame = requestAnimationFrame(animateLine);
pauseButton.addEventListener("click", () => { const paused = pauseButton.toggleAttribute("aria-pressed"); pauseButton.textContent = paused ? "Play" : "Pause"; if (paused) { cancelAnimationFrame(frame); } else { resumed = true; frame = requestAnimationFrame(animateLine); } });});What to notice
Section titled “What to notice”updateLayerData(layerId, geojson)targets a layer; YAML authors never name the internal source the layer was given.- For data that arrives from a server rather than a loop, prefer the declarative
refresh:orstream:source options — see Add live realtime data.