Динамическое изменение стиля

Визуальное оформление карты в Mapbox GL JS определяется стилем (style), представляющим собой JSON-спецификацию по стандарту Mapbox Style Specification. Стиль описывает источники данных, слои, типы рендеринга, шрифты, спрайты и множество визуальных параметров. Одной из ключевых возможностей является динамическое изменение стиля во время работы приложения без перезагрузки страницы и без пересоздания карты.

Базовый механизм смены стиля

Основной метод изменения стиля:

map.setStyle('mapbox://styles/mapbox/streets-v12');

После вызова происходит полная перезагрузка графического состояния карты: очищаются текущие слои, источники и перезапускается процесс загрузки нового style.json.

Важно учитывать, что смена стиля — это не косметическое обновление, а фактически пересборка всей карты.


Жизненный цикл смены стиля

При вызове setStyle происходит последовательность внутренних этапов:

  • выгрузка текущих слоев и источников
  • загрузка нового style JSON
  • загрузка sprite и glyphs
  • пересоздание WebGL-слоя
  • генерация события styledata
  • финальное событие style.load

Ключевым моментом является то, что любые добавленные вручную слои и источники исчезают после смены стиля.


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

Для корректной работы после смены стиля используется событие:

map.on('style.load', () => {
  // повторное добавление слоев и источников
});

Также часто применяется:

map.on('styledata', () => {
  // промежуточное состояние загрузки стиля
});

style.load вызывается один раз после полной загрузки нового стиля, тогда как styledata может вызываться многократно в процессе загрузки ресурсов.


Потеря пользовательских слоев и источников

После вызова setStyle все следующие элементы удаляются:

  • пользовательские sources
  • пользовательские layers
  • кастомные изображения (addImage)
  • события, связанные с источниками данных

Это поведение требует архитектурного подхода к повторной инициализации.


Восстановление состояния карты

Типовой подход заключается в хранении описания пользовательских слоев и источников:

const customLayers = [
  {
    id: 'points-layer',
    type: 'circle',
    source: 'points-source',
    paint: {
      'circle-radius': 6,
      'circle-color': '#ff0000'
    }
  }
];

const customSources = {
  'points-source': {
    type: 'geojson',
    data: '/data/points.geojson'
  }
};

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

map.on('style.load', () => {
  Object.entries(customSources).forEach(([id, source]) => {
    map.addSource(id, source);
  });

  customLayers.forEach(layer => {
    map.addLayer(layer);
  });
});

Изменение стиля без потери источников (переиспользование данных)

Существует способ частично сохранить данные источников, если новый стиль совместим по структуре:

const currentSources = map.getStyle().sources;
const currentLayers = map.getStyle().layers;

map.setStyle(newStyleUrl);

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


Смена стиля и сохранение пользовательских изображений

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

map.loadImage('/icon.png', (error, image) => {
  if (!error) map.addImage('custom-icon', image);
});

После смены стиля изображения исчезают и требуют повторной загрузки:

map.on('style.load', () => {
  map.loadImage('/icon.png', (error, image) => {
    if (!error && !map.hasImage('custom-icon')) {
      map.addImage('custom-icon', image);
    }
  });
});

Плавное переключение стилей

Резкая смена стиля может вызывать визуальные скачки. Для сглаживания применяется промежуточная логика:

  • временное скрытие слоев
  • использование opacity transition
  • предварительная загрузка стиля

Пример:

map.setPaintProperty('water-layer', 'fill-opacity', 0);

map.once('style.load', () => {
  map.setPaintProperty('water-layer', 'fill-opacity', 1);
});

Проверка готовности стиля

Состояние загрузки контролируется через:

if (map.isStyleLoaded()) {
  // безопасно работать со слоями
}

Это предотвращает ошибки добавления слоев до завершения загрузки ресурсов.


Переключение между несколькими стилями

Типовой сценарий — переключение между темной и светлой темой:

const styles = {
  light: 'mapbox://styles/mapbox/light-v11',
  dark: 'mapbox://styles/mapbox/dark-v11'
};

function switchStyle(mode) {
  map.setStyle(styles[mode]);
}

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


Использование событий для восстановления сложных слоёв

При наличии сложных визуальных схем применяется централизованная функция восстановления:

function restoreMapState() {
  addSources();
  addLayers();
  addControls();
  addImages();
}

map.on('style.load', restoreMapState);

Подобный подход упрощает сопровождение при наличии нескольких стилей.


Особенности работы с выражениями (expressions) при смене стиля

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

  • выражения, заданные в слоях, удаляются вместе с ними
  • новые выражения должны быть заново применены при добавлении слоя
  • невозможно частично «перенести» выражение между стилями без пересоздания слоя

Пример:

const layer = {
  id: 'population',
  type: 'fill',
  source: 'states',
  paint: {
    'fill-color': [
      'interpolate',
      ['linear'],
      ['get', 'pop_density'],
      0, '#f2f0f7',
      1000, '#cbc9e2',
      5000, '#6a51a3'
    ]
  }
};

Оптимизация переключения стилей

При частой смене стилей учитываются следующие факторы:

  • предварительная загрузка стилей через скрытую карту
  • минимизация тяжелых GeoJSON источников
  • повторное использование изображений и шрифтов
  • контроль количества слоев

Предварительная инициализация:

const preloadedMap = new mapboxgl.Map({
  container: 'hidden',
  style: 'mapbox://styles/mapbox/dark-v11'
});

Работа с источниками данных при смене стиля

Источники данных делятся на:

  • GeoJSON
  • Vector tiles
  • Raster tiles
  • Image sources

При смене стиля:

  • данные источников не сохраняются
  • URL источников может оставаться актуальным, но требуется повторное объявление через addSource
map.addSource('cities', {
  type: 'geojson',
  data: '/data/cities.geojson'
});

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

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

  1. фиксация текущего состояния
  2. вызов setStyle
  3. ожидание style.load
  4. восстановление слоёв
  5. применение визуальных корректировок

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

Смена стиля не влияет на:

  • положение камеры (center)
  • масштаб (zoom)
  • наклон (pitch)
  • поворот (bearing)

Это позволяет отделять визуальное оформление от географического состояния.


Ошибки и нестабильные состояния

Частые источники проблем:

  • добавление слоёв до style.load
  • обращение к несуществующим источникам
  • повторная регистрация одинаковых ID
  • асинхронная загрузка изображений

Корректная синхронизация выполняется через события загрузки стиля и проверку наличия ресурсов:

if (!map.getSource('points')) {
  map.addSource('points', sourceConfig);
}