События и состояние карты

Событийная модель построена на расширенной реализации Evented, аналогичной DOM-событиям, но специализированной для состояния карты и графического слоя WebGL.

Базовый механизм подписки

Каждый экземпляр карты предоставляет методы:

  • on(type, listener) — подписка на событие
  • off(type, listener) — удаление обработчика
  • once(type, listener) — одноразовая подписка

Тип события может включать пространство имён через точку, что позволяет структурировать обработчики:

map.on('click', (e) => {
  console.log(e.lngLat);
});

map.on('moveend', handleMoveEnd);
map.once('load', initLayers);

Пространства имён применяются для группового управления:

map.on('click.markers', handler);
map.off('click.markers');

Событие загрузки карты и стиля

load

Срабатывает после полной загрузки ресурсов карты, включая стиль, источники и базовые тайлы.

map.on('load', () => {
  map.addLayer({
    id: 'points',
    type: 'circle',
    source: 'points-source'
  });
});

Особенность: событие не гарантирует готовность всех динамически добавленных ресурсов после инициализации, особенно при смене стиля.


style.load

Более узкое событие, срабатывающее при загрузке нового стиля, включая setStyle.

map.on('style.load', () => {
  console.log('Стиль загружен');
});

Используется для реактивации слоёв после смены визуальной темы карты.


События движения карты

Состояние камеры контролируется через изменения центра, масштаба, наклона и поворота.

move

Срабатывает при любом изменении положения камеры.

map.on('move', () => {
  const center = map.getCenter();
});

Используется для синхронизации UI в реальном времени, но требует осторожности из-за высокой частоты вызова.


movestart и moveend

Разделяют процесс перемещения на начало и конец.

map.on('movestart', () => {
  console.log('Начало движения');
});

map.on('moveend', () => {
  console.log('Движение завершено');
});

moveend часто используется для загрузки данных по новому экстенту карты.


zoom и zoomend

Отслеживание масштабирования:

map.on('zoom', () => {
  console.log(map.getZoom());
});

rotate и pitch

Отслеживают изменение угла вращения и наклона:

map.on('rotate', () => {});
map.on('pitch', () => {});

События рендеринга

render

Срабатывает при каждом кадре перерисовки WebGL-сцены.

Используется для анимаций и синхронизации внешних слоёв:

map.on('render', () => {
  // вызывается часто
});

Важно учитывать, что событие может вызываться десятки раз в секунду.


idle

Срабатывает, когда карта полностью «успокаивается»: нет движения, загрузки тайлов и перерисовки.

map.on('idle', () => {
  console.log('Карта стабилизировалась');
});

Это ключевое событие для определения завершения всех асинхронных процессов.


Взаимодействие с мышью и касаниями

click

Основное событие выбора объектов:

map.on('click', (e) => {
  const features = map.queryRenderedFeatures(e.point);
});

Объект события содержит:

  • lngLat — географические координаты
  • point — координаты в пикселях
  • features — пересечённые объекты слоя (при запросе)

mousemove и mouseenter/mouseleave

Используются для интерактивных подсказок:

map.on('mousemove', 'layer-id', (e) => {
  map.getCanvas().style.cursor = 'pointer';
});

Особенность: события могут привязываться к конкретному слою.


mousedown / mouseup

Низкоуровневое управление взаимодействием:

map.on('mousedown', (e) => {});
map.on('mouseup', (e) => {});

Применяется при реализации кастомного drag-логики.


События источников данных

data

Срабатывает при загрузке или обновлении данных любого источника.

map.on('data', (e) => {
  if (e.sourceId === 'cities') {
    console.log('Обновление источника cities');
  }
});

source.load и source.data

Позволяют отслеживать состояние конкретных источников:

map.on('sourcedata', (e) => {
  if (e.isSourceLoaded) {
    console.log('Источник загружен');
  }
});

События ошибок

error

Универсальное событие для всех ошибок карты:

map.on('error', (e) => {
  console.error(e.error);
});

Ошибки могут быть связаны с:

  • загрузкой тайлов
  • некорректными стилями
  • отсутствующими источниками

Состояние карты

Состояние карты представляет собой совокупность параметров камеры, стиля и загрузки ресурсов.

Камера

Основные параметры:

  • центр
  • zoom
  • bearing
  • pitch

Получение значений:

map.getCenter();
map.getZoom();
map.getBearing();
map.getPitch();

Установка состояния:

map.setCenter([30.5, 50.4]);
map.setZoom(10);
map.setBearing(45);
map.setPitch(30);

Границы отображения

const bounds = map.getBounds();

Используется для:

  • загрузки данных по текущему экстенту
  • фильтрации объектов
  • синхронизации с сервером

Проверка загрузки

map.loaded();
map.isStyleLoaded();

loaded() возвращает состояние готовности всех ресурсов.


Управление состоянием камеры

flyTo

Анимационный переход:

map.flyTo({
  center: [30, 50],
  zoom: 12,
  speed: 1.2
});

easeTo

Более мягкая анимация без резких ускорений:

map.easeTo({
  center: [30, 50],
  duration: 800
});

jumpTo

Мгновенное изменение состояния:

map.jumpTo({
  center: [30, 50],
  zoom: 10
});

Синхронизация состояния

Часто состояние карты синхронизируют с URL или внешним состоянием приложения.

Пример синхронизации с хэшем

map.on('moveend', () => {
  const center = map.getCenter();
  const zoom = map.getZoom();

  location.hash = `${zoom}/${center.lng}/${center.lat}`;
});

При инициализации:

const [zoom, lng, lat] = location.hash.slice(1).split('/');
map.setView([lng, lat], zoom);

Производительность событий

События движения (move, render, zoom) могут генерироваться с высокой частотой.

Типичные подходы оптимизации:

  • ограничение частоты через requestAnimationFrame
  • debounce на moveend
  • минимизация логики внутри обработчиков
let ticking = false;

map.on('move', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      updateUI();
      ticking = false;
    });
    ticking = true;
  }
});

Управление подписками и утечками памяти

Долгоживущие приложения требуют явного удаления обработчиков:

function handler() {}

map.on('click', handler);
map.off('click', handler);

При использовании пространств имён:

map.off('click.layerHandlers');

Связь событий и слоёв

События могут быть привязаны к конкретным слоям:

map.on('click', 'cities-layer', (e) => {
  console.log(e.features);
});

Это позволяет:

  • работать только с релевантными объектами
  • уменьшать необходимость фильтрации вручную
  • повышать точность интерактивности

Состояние как источник истины

Карта в Mapbox GL JS рассматривается как трансформируемая сцена WebGL, где текущее состояние определяется комбинацией:

  • параметров камеры
  • загруженных источников
  • активного стиля
  • состояния рендер-цикла

Любое событие либо изменяет это состояние, либо сигнализирует о его изменении, формируя непрерывный поток обновлений, на котором строится интерактивность и реактивная логика приложения.