Reactive properties

Mapbox GL JS строит отрисовку карты как непрерывно обновляемое состояние, где любые изменения параметров карты приводят к перерасчёту кадра. Основная идея заключается в том, что карта не «рисуется вручную», а описывается через набор свойств состояния и декларативную стилевую модель.

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

Состояние карты включает несколько уровней:

  • геометрическое положение (центр, масштаб, поворот, наклон)
  • стили отображения (слои, источники данных, выражения)
  • данные (GeoJSON, векторные тайлы, растровые источники)
  • параметры взаимодействия и событий

Каждый из этих уровней связан с внутренним механизмом пересборки сцены.


Геометрические свойства как реактивные параметры

Основной набор свойств карты задаёт её положение и ориентацию. Эти параметры являются реактивными: любое изменение вызывает перерасчёт матрицы проекции и перерисовку сцены.

Центр карты

map.setCenter([37.6173, 55.7558]);

Свойство центра определяет координату, вокруг которой строится визуализация. Изменение центра мгновенно приводит к перерасчёту тайлов, попадающих в область видимости.

Масштаб

map.setZoom(10);

Масштаб влияет на уровень детализации данных. При изменении zoom происходит:

  • выбор новых тайлов
  • перерасчёт кластеризации (если применимо)
  • переключение уровней детализации слоёв

Поворот и наклон

map.setBearing(45);
map.setPitch(60);

Эти параметры изменяют матрицу камеры. Внутренний рендеринг пересчитывает перспективную проекцию, что делает возможным 3D-эффекты и наклоненные карты.


Состояние стиля как реактивная структура

Стиль карты в Mapbox GL JS представляет собой JSON-структуру, которая полностью описывает визуальное представление:

  • источники данных (sources)
  • слои (layers)
  • фильтры
  • выражения (expressions)

Изменение стиля не является точечной операцией — это атомарная замена или дифф.

map.setStyle('mapbox://styles/mapbox/dark-v11');

При смене стиля происходит:

  • удаление текущих слоёв
  • загрузка нового style JSON
  • повторная инициализация источников
  • пересборка графа рендеринга

Это делает стиль полностью реактивным объектом: любое изменение приводит к перестроению визуальной сцены.


Динамическое обновление свойств слоёв

Отдельные свойства слоёв можно изменять без пересборки всего стиля.

Paint properties

map.setPaintProperty('water', 'fill-color', '#3366ff');

Изменение paint-параметров влияет на отрисовку конкретного слоя:

  • цвет
  • прозрачность
  • градиенты
  • ширина линий

Эти изменения применяются в GPU-пайплайне без полной перезагрузки стиля.

Layout properties

map.setLayoutProperty('road-label', 'visibility', 'none');

Layout-свойства влияют на структуру слоя:

  • видимость
  • символика
  • размещение текстов
  • порядок отрисовки

Изменения layout чаще требуют перерасчёта размещения символов.


Данные как реактивный источник состояния

Источники данных в Mapbox GL JS также обладают реактивной природой.

GeoJSON source

map.getSource('points').setData({
  type: 'FeatureCollection',
  features: []
});

Обновление данных приводит к:

  • пересчёту геометрии
  • обновлению кластеров
  • пересборке индексов отрисовки

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


Событийная модель как слой реактивности

Mapbox GL JS использует событийную систему, которая отражает изменения внутреннего состояния:

map.on('move', () => {});
map.on('zoom', () => {});
map.on('data', () => {});
map.on('render', () => {});

События выполняют роль триггеров реактивного обновления внешнего кода.

Ключевые категории событий:

  • camera events: move, zoom, rotate
  • data events: sourcedata, styledataloading
  • render events: render, idle

Событие render особенно важно, так как оно вызывается на каждом кадре при изменении состояния.


Выражения (Expressions) как реактивная система внутри стиля

Mapbox GL JS содержит встроенный язык выражений, который позволяет описывать динамическое поведение слоёв.

Интерполяция

'circle-radius': [
  'interpolate',
  ['linear'],
  ['zoom'],
  5, 2,
  10, 10
]

Размер точки изменяется в зависимости от zoom. Это декларативная реактивность: значение зависит от состояния карты.


Привязка к свойствам данных

'circle-color': [
  'get',
  'color'
]

Цвет вычисляется на основе свойства GeoJSON-объекта. Изменение данных автоматически изменяет визуализацию.


Условная логика

'line-width': [
  'case',
  ['>', ['get', 'traffic'], 0.5],
  5,
  2
]

Слой реагирует на значения данных без внешнего кода.


Внутренний цикл рендеринга как механизм реактивности

Mapbox GL JS использует непрерывный цикл отрисовки, который активируется при изменении состояния.

Упрощённо процесс выглядит так:

  1. изменение состояния (центр, стиль, данные)
  2. пометка сцены как “dirty”
  3. пересборка зависимых слоёв
  4. перерасчёт тайлов и буферов
  5. отрисовка кадра

Если состояние стабильно, рендеринг переходит в режим idle.


Частичная реактивность через источники и слои

Разделение состояния на источники и слои позволяет добиться локальной реактивности.

  • изменение GeoJSON → обновляется только связанный слой
  • изменение paint → обновляется GPU-параметр
  • изменение style → пересборка всей сцены

Это предотвращает избыточные вычисления.


Синхронизация внешнего состояния

Карты часто используются как часть более крупного состояния приложения. Реактивная модель позволяет синхронизировать параметры карты с внешними системами.

Пример синхронизации параметров камеры:

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

Состояние карты становится источником истины для внешнего UI.


Реактивность и производительность

Реактивная модель требует контроля над частотой обновлений.

Типичные узкие места:

  • частое обновление GeoJSON
  • изменение style целиком вместо точечных свойств
  • неконтролируемые события move и render

Оптимизация достигается через:

  • батчинг обновлений данных
  • использование setPaintProperty вместо setStyle
  • ограничение частоты обработчиков событий
  • использование requestAnimationFrame для синхронизации внешних обновлений

Динамическая фильтрация как реактивное поведение слоёв

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

map.setFilter('points', ['==', ['get', 'type'], 'restaurant']);

Изменение фильтра мгновенно перерассчитывает набор отображаемых объектов.


Слой как функция от состояния

Каждый слой можно рассматривать как функцию:

Layer = f(source, zoom, state, expressions)

Любое изменение аргументов функции приводит к пересчёту результата. Это и есть основа реактивной модели Mapbox GL JS.


Реактивные зависимости между слоями

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

  • кластерный слой зависит от zoom
  • символический слой зависит от visibility базового слоя
  • heatmap зависит от density источника

Изменение одного параметра может каскадно влиять на несколько слоёв, создавая граф реактивных зависимостей внутри стиля.


Управление состоянием через URL и внешние источники

Камера карты часто синхронизируется с URL-параметрами:

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

Такое поведение делает состояние карты сериализуемым и восстанавливаемым.


Итеративные обновления как форма реактивного потока

Вместо единичных изменений состояние карты часто обновляется потоково:

  • движение мыши → непрерывное обновление камеры
  • поток данных → обновление GeoJSON
  • пользовательские фильтры → мгновенное изменение слоёв

Это создаёт непрерывную реактивную систему, в которой карта функционирует как живой граф состояния.