Работа с маркерами в интерактивных картах HERE Technologies в контексте HERE Maps API for JavaScript строится вокруг объектов слоя отображения карты, которые можно динамически изменять во времени. Анимация маркеров в этом API не является отдельным высокоуровневым модулем, а реализуется через комбинацию изменения географических координат, управления сценой рендеринга и использования браузерных механизмов перерисовки.
Маркер в JavaScript API HERE представляет собой объект, связанный с
координатой H.geo.Point. При каждом изменении координаты
карта перерасчитывает позицию объекта на экране и перерисовывает
соответствующий слой.
Ключевая особенность заключается в том, что:
Объект маркера:
const marker = new H.map.Marker({ lat: 52.5, lng: 13.4 });
map.addObject(marker);
Изменение позиции выполняется через setGeometry:
marker.setGeometry({ lat: 52.51, lng: 13.41 });
Каждый вызов setGeometry инициирует перерасчёт сцены,
поэтому частота обновлений становится критически важной для
производительности.
Плавное движение достигается вычислением промежуточных точек между начальной и конечной координатой. Линейная интерполяция применяется отдельно к широте и долготе:
function lerp(start, end, t) {
return start + (end - start) * t;
}
Для географических координат:
const lat = lerp(startLat, endLat, t);
const lng = lerp(startLng, endLng, t);
Где t изменяется от 0 до 1.
Такой подход создаёт прямолинейную траекторию, которая в большинстве случаев используется для упрощённых анимаций объектов, например, транспортных маркеров или симуляции движения.
Основной механизм синхронизации анимации с рендерингом браузера
строится на requestAnimationFrame. Это позволяет избежать
лишних перерисовок и обеспечивает стабильную частоту кадров.
function animateMarker(marker, start, end, duration) {
const startTime = performance.now();
function frame(time) {
const progress = Math.min((time - startTime) / duration, 1);
const lat = lerp(start.lat, end.lat, progress);
const lng = lerp(start.lng, end.lng, progress);
marker.setGeometry({ lat, lng });
if (progress < 1) {
requestAnimationFrame(frame);
}
}
requestAnimationFrame(frame);
}
Такой цикл обеспечивает:
При работе с большим количеством маркеров критическим становится
вопрос производительности. Каждый вызов setGeometry
приводит к:
Практика оптимизации включает:
1. Снижение частоты обновлений Не обязательно обновлять координаты на каждом кадре — допустимо обновление каждые 2–3 кадра при высокой нагрузке.
2. Батчинг обновлений Групповое обновление нескольких маркеров в одном цикле анимации снижает overhead рендеринга.
3. Использование лёгких DOM-структур При
необходимости визуальных эффектов предпочтение отдаётся
H.map.DomMarker, но он дороже в рендеринге по сравнению с
H.map.Marker.
Более сложный сценарий — движение маркера по полилинии маршрута. В
этом случае используется набор точек LineString, а движение
интерполируется по сегментам.
const lineString = new H.geo.LineString();
lineString.pushPoint({ lat: 52.5, lng: 13.4 });
lineString.pushPoint({ lat: 52.6, lng: 13.5 });
lineString.pushPoint({ lat: 52.7, lng: 13.6 });
Далее рассчитывается позиция вдоль линии:
Такой подход используется в системах:
Для повышения реалистичности часто требуется синхронизация ориентации маркера с направлением движения. В API это достигается через CSS-трансформации (для DOM-маркеров) или через иконки с угловыми состояниями.
Расчёт угла:
function getBearing(start, end) {
const dLng = end.lng - start.lng;
const dLat = end.lat - start.lat;
return Math.atan2(dLng, dLat) * 180 / Math.PI;
}
Применение:
const element = marker.getElement();
element.style.transform = `rotate(${bearing}deg)`;
Такой метод применим только к DomMarker, поскольку
стандартные маркеры используют растровые или SVG-иконки без динамической
трансформации.
Линейная интерполяция создаёт равномерное движение, которое часто выглядит механически. Для улучшения визуального восприятия применяются easing-функции.
Пример ускорения и замедления:
function easeInOut(t) {
return t < 0.5
? 2 * t * t
: -1 + (4 - 2 * t) * t;
}
Использование:
const eased = easeInOut(progress);
const lat = lerp(start.lat, end.lat, eased);
const lng = lerp(start.lng, end.lng, eased);
Easing особенно важен при имитации движения транспорта, где физическая инерция должна быть визуально ощутима.
При одновременной анимации множества объектов возникает проблема синхронизации обновлений. Основной подход — централизованный менеджер анимаций.
Логика включает:
animation loop;const animations = [];
function tick(time) {
animations.forEach(anim => anim.update(time));
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
Каждый объект содержит собственное состояние прогресса и не инициирует отдельный цикл, что значительно снижает нагрузку на браузер.
В реальных сценариях данные часто приходят из внешних источников (GPS-трекинг, телеметрия). В таких случаях важно использовать серверное время или timestamp события.
Это позволяет:
Формат данных:
{
lat: 52.5,
lng: 13.4,
timestamp: 1710000000000
}
Анимация строится не от кадрового времени, а от разницы временных меток, что обеспечивает детерминированность движения.
Визуально сложные сцены включают синхронное движение маркера и карты. Камера карты обновляется через:
map.getViewModel().setLookAtData({
position: { lat, lng },
zoom: 14
});
При этом важно избегать конфликтов между:
Обычно вводится режим «следования за объектом», при котором управление блокируется или ограничивается.
При высокой частоте обновления данных (например, каждые 100–200 мс) основным ограничением становится не математика интерполяции, а рендеринг DOM/WebGL слоя.
Ключевые ограничения:
setGeometry.Практическая стратегия масштабирования заключается в переходе от индивидуальной анимации каждого маркера к пакетной визуализации или кластеризации при отдалении карты.
Помимо стандартного обновления координат, применяются:
Каждый из подходов снижает нагрузку на API объектов карты, но увеличивает сложность реализации логики синхронизации.