Анимация векторных объектов в OpenLayers опирается на механизм перерисовки слоёв и динамическое изменение геометрии или стиля объектов. Векторный слой (Vector Layer) в библиотеке не является статичным: каждый кадр может пересчитываться визуальное представление features, что позволяет реализовать движение, морфинг форм, пульсации и визуальные эффекты высокой сложности.
В основе анимации лежит цикл отрисовки карты. Карта вызывает перерисовку слоя в следующих случаях:
render()Ключевой момент: OpenLayers не анимирует объекты автоматически — разработчик управляет состоянием объектов, а библиотека лишь отображает изменения.
Наиболее прямолинейный способ анимации — изменение координат геометрии во времени.
import Feature from 'ol/Feature.js';
import Point from 'ol/geom/Point.js';
const feature = new Feature({
geometry: new Point([0, 0])
});
let x = 0;
function animate() {
x += 0.01;
const geometry = feature.getGeometry();
geometry.setCoordinates([x, Math.sin(x)]);
requestAnimationFrame(animate);
}
animate();
requestAnimationFrameОсновной механизм синхронизации анимации —
requestAnimationFrame, который:
setIntervalТипичный цикл:
function loop(timestamp) {
updateState(timestamp);
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
Резкое изменение координат приводит к «дёрганому» движению. Для сглаживания используется интерполяция.
function lerp(a, b, t) {
return a + (b - a) * t;
}
const start = [0, 0];
const end = [10, 10];
function animate(t) {
const k = Math.min(t / 1000, 1);
const coord = [
lerp(start[0], end[0], k),
lerp(start[1], end[1], k)
];
feature.getGeometry().setCoordinates(coord);
requestAnimationFrame((ts) => animate(ts));
}
Изменение геометрии — не единственный подход. Более лёгкий способ для визуальных эффектов — динамический стиль.
import Style from 'ol/style/Style.js';
import CircleStyle from 'ol/style/Circle.js';
import Fill from 'ol/style/Fill.js';
let t = 0;
const style = new Style({
image: new CircleStyle({
radius: 10,
fill: new Fill({ color: 'rgba(255,0,0,0.5)' })
})
});
feature.setStyle(() => {
const radius = 10 + Math.sin(t) * 5;
t += 0.1;
return new Style({
image: new CircleStyle({
radius,
fill: new Fill({ color: 'rgba(255,0,0,0.5)' })
})
});
});
Частый кейс — «рисование» линии по мере продвижения объекта.
const fullRoute = [
[0, 0],
[5, 5],
[10, 10]
];
let index = 0;
const lineFeature = new Feature({
geometry: new LineString([])
});
function step() {
index++;
lineFeature.getGeometry().setCoordinates(
fullRoute.slice(0, index)
);
if (index < fullRoute.length) {
requestAnimationFrame(step);
}
}
step();
Для более реалистичной анимации используется параметр времени вдоль линии.
Геометрия может оставаться неизменной, но стиль — вращаться.
import Icon from 'ol/style/Icon.js';
let angle = 0;
feature.setStyle(() => {
angle += 0.05;
return new Style({
image: new Icon({
src: 'arrow.png',
rotation: angle
})
});
});
Применение:
Эффект пульса достигается изменением радиуса или прозрачности.
let alpha = 1;
let direction = -0.05;
feature.setStyle(() => {
alpha += direction;
if (alpha < 0.3 || alpha > 1) {
direction *= -1;
}
return new Style({
image: new CircleStyle({
radius: 8,
fill: new Fill({
color: `rgba(0, 150, 255, ${alpha})`
})
})
});
});
При десятках и сотнях объектов важно учитывать производительность.
let globalTime = 0;
function tick() {
globalTime += 1;
features.forEach(f => {
const geom = f.getGeometry();
// обновление состояния
});
requestAnimationFrame(tick);
}
При работе с картой важно учитывать проекции.
Используется:
import { fromLonLat } from 'ol/proj.js';
const coord = fromLonLat([37.6173, 55.7558]);
Анимация должна выполняться уже в целевой проекции, иначе движение будет искажено.
Для реалистичных движений применяются easing-функции.
function easeInOut(t) {
return t < 0.5
? 2 * t * t
: -1 + (4 - 2 * t) * t;
}
Использование:
Более сложный тип анимации — трансформация формы.
Пример: изменение полигона во времени
function morph(a, b, t) {
return a.map((coord, i) => [
lerp(coord[0], b[i][0], t),
lerp(coord[1], b[i][1], t)
]);
}
Вместо самостоятельного requestAnimationFrame можно
использовать:
map.on('postrender', function () {
// обновление состояния
feature.changed();
});
Это позволяет синхронизировать анимацию с отрисовкой карты и снижает рассинхронизацию кадров.
Для визуализации потоков используются точки, движущиеся вдоль линий.
particles.forEach(p => {
p.t += 0.01;
p.coord = line.getCoordinateAt(p.t);
});
Векторная анимация в OpenLayers сводится к нескольким базовым стратегиям:
requestAnimationFrameКаждый подход выбирается в зависимости от нагрузки, количества объектов и требуемого визуального эффекта.