В MapLibre GL JS вся работа с «движением» карты строится вокруг камеры. Камера описывает текущее состояние отображения: центр, масштаб, угол поворота и наклон. Любое изменение этих параметров считается движением, даже если визуально карта остаётся почти неподвижной.
Ключевые параметры камеры:
Эти параметры можно получить напрямую:
const center = map.getCenter();
const zoom = map.getZoom();
const bearing = map.getBearing();
const pitch = map.getPitch();
Любая анимация или пользовательское взаимодействие сводится к изменению одного или нескольких этих значений.
Отслеживание движения начинается с событий. MapLibre GL JS предоставляет набор событий, позволяющих реагировать на изменения камеры в реальном времени.
Основные события:
movestart — начало любого движенияmove — изменение положения камерыmoveend — завершение движенияzoomstart, zoom, zoomend —
масштабированиеrotatestart, rotate,
rotateend — вращениеpitchstart, pitch, pitchend —
наклонmap.on('move', () => {
const center = map.getCenter();
console.log('Центр:', center.lng, center.lat);
});
Событие move вызывается очень часто, поэтому его
используют только для лёгких операций.
map.on('moveend', () => {
const state = {
center: map.getCenter(),
zoom: map.getZoom(),
bearing: map.getBearing(),
pitch: map.getPitch()
};
console.log('Финальное состояние камеры:', state);
});
moveend применяется для фиксации итогового положения
камеры после анимации или взаимодействия пользователя.
MapLibre не разделяет явно источник движения, но его можно различать через контекст событий.
let isProgrammatic = false;
function moveCamera() {
isProgrammatic = true;
map.easeTo({
center: [30, 50],
zoom: 10
});
setTimeout(() => {
isProgrammatic = false;
}, 300);
}
map.on('move', () => {
if (isProgrammatic) {
console.log('Движение вызвано кодом');
} else {
console.log('Движение пользователя');
}
});
Такой подход используется для аналитики и синхронизации интерфейсов.
Для анимации движения используются методы:
flyTo — полёт к точкеeaseTo — плавное перемещениеjumpTo — мгновенное перемещениеmap.easeTo({
center: [60, 55],
zoom: 8,
duration: 1000
});
map.flyTo({
center: [60, 55],
zoom: 9,
speed: 1.2,
curve: 1.5
});
flyTo используется, когда нужно имитировать естественное
движение камеры с дугообразной траекторией.
Иногда требуется фиксировать положение камеры с максимальной точностью, например для синхронизации с 3D-объектами или анимациями интерфейса.
function trackCamera() {
const center = map.getCenter();
const zoom = map.getZoom();
const bearing = map.getBearing();
updateOverlay(center, zoom, bearing);
requestAnimationFrame(trackCamera);
}
trackCamera();
Такой подход не зависит от событий MapLibre и позволяет получать состояние камеры каждый кадр.
При отслеживании move события данные приходят с высокой
частотой и могут выглядеть «рваными» при визуализации.
Для сглаживания используется интерполяция:
let lastCenter = null;
map.on('move', () => {
const current = map.getCenter();
if (lastCenter) {
const smoothLng = lastCenter.lng * 0.8 + current.lng * 0.2;
const smoothLat = lastCenter.lat * 0.8 + current.lat * 0.2;
renderMarker([smoothLng, smoothLat]);
}
lastCenter = current;
});
Такой метод снижает резкость изменений и подходит для визуальных элементов, следующих за камерой.
Частая задача — синхронизация DOM-элементов с движением карты: тултипы, панели, индикаторы.
map.on('move', () => {
const center = map.getCenter();
infoBox.textContent = `
lng: ${center.lng.toFixed(4)}
lat: ${center.lat.toFixed(4)}
`;
});
При необходимости можно учитывать масштаб:
map.on('move', () => {
const zoom = map.getZoom();
scaleIndicator.style.width = `${zoom * 10}px`;
});
Для анализа пользовательского поведения часто требуется сохранять путь движения камеры.
const path = [];
map.on('move', () => {
const center = map.getCenter();
path.push({
lng: center.lng,
lat: center.lat,
time: Date.now()
});
});
Далее массив может использоваться для построения линии движения или аналитики.
Режим «следования» фиксирует камеру на определённой точке, например на объекте или координате GPS.
let follow = true;
function updatePosition(lng, lat) {
if (follow) {
map.easeTo({
center: [lng, lat],
duration: 500
});
}
}
Такой режим часто используется в навигационных системах.
MapLibre GL JS поддерживает встроенный контрол геолокации.
map.addControl(
new maplibregl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
})
);
При включённом trackUserLocation карта автоматически
обновляет позицию пользователя, что фактически превращает её в систему
слежения за движением в реальном мире.
Скорость перемещения можно вычислить на основе изменения центра между событиями.
let last = null;
map.on('move', () => {
const current = map.getCenter();
const time = Date.now();
if (last) {
const dx = current.lng - last.center.lng;
const dy = current.lat - last.center.lat;
const dt = time - last.time;
const speed = Math.sqrt(dx * dx + dy * dy) / dt;
console.log('Скорость движения камеры:', speed);
}
last = { center: current, time };
});
Такие вычисления применяются в аналитике пользовательского поведения и адаптивных интерфейсах.
Отслеживание движения используется для связывания нескольких экземпляров карты.
function sync(source, target) {
source.on('move', () => {
const center = source.getCenter();
const zoom = source.getZoom();
const bearing = source.getBearing();
target.jumpTo({ center, zoom, bearing });
});
}
sync(mapA, mapB);
При этом важно учитывать рекурсивные обновления, иначе карты начнут бесконечно вызывать события друг друга.
Иногда требуется полностью зафиксировать камеру:
map.dragPan.disable();
map.scrollZoom.disable();
map.boxZoom.disable();
map.keyboard.disable();
После этого движение возможно только программно:
map.easeTo({
center: [40, 40],
zoom: 5
});
Такой режим используется для презентаций и сценариев с управляемой навигацией.
После пользовательского взаимодействия карта может продолжать
движение по инерции. Для фиксации окончания используется
moveend.
map.on('moveend', () => {
console.log('Камера стабилизировалась');
});
Это событие критично для запуска логики, зависящей от финального положения.
При использовании наклона (pitch) и вращения
(bearing) важно синхронизировать 3D-объекты с камерой.
map.on('move', () => {
const bearing = map.getBearing();
model.rotation.z = -bearing * Math.PI / 180;
});
Без такой синхронизации 3D-слои будут визуально «отставать» от карты.
Для оптимизации нагрузки обработку move часто
ограничивают:
let ticking = false;
map.on('move', () => {
if (!ticking) {
requestAnimationFrame(() => {
const center = map.getCenter();
updateUI(center);
ticking = false;
});
ticking = true;
}
});
Это предотвращает перегрузку при частых событиях.
Движение карты может использоваться как источник времени для анимации интерфейса:
map.on('move', () => {
const zoom = map.getZoom();
const scale = Math.pow(2, zoom);
element.style.transform = `scale(${scale})`;
});
Таким образом камера становится управляющим фактором всей сцены отображения.