В Mapbox GL JS визуальное оформление карты полностью определяется объектом стиля. Стиль задаёт источники данных, слои визуализации, шрифты, спрайты и базовые параметры рендеринга. Загрузка пользовательского стиля — ключевой этап построения интерактивных карт, поскольку именно на этом уровне формируется внешний вид и поведение всей сцены WebGL.
В Mapbox GL JS поддерживаются два основных способа задания стиля: через URL и через объект JSON.
Стиль по URL
Наиболее распространённый вариант — использование хостинга Mapbox:
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [37.6173, 55.7558],
zoom: 10
});
URL указывает на заранее опубликованный стиль в Mapbox Studio. Такой подход обеспечивает автоматическое обновление ресурсов (слоёв, тайлов, спрайтов и шрифтов) без необходимости ручного управления зависимостями.
Стиль как объект
Альтернативный вариант — передача полного JSON-объекта стиля:
const customStyle = {
version: 8,
sources: {
cities: {
type: 'geojson',
data: '/data/cities.geojson'
}
},
layers: [
{
id: 'cities-layer',
type: 'circle',
source: 'cities',
paint: {
'circle-radius': 6,
'circle-color': '#ff5200'
}
}
]
};
const map = new mapboxgl.Map({
container: 'map',
style: customStyle
});
Такой подход используется при динамическом формировании стиля или работе с локальными данными без подключения к Mapbox Studio.
Любой стиль Mapbox GL JS базируется на спецификации Style Specification v8.
Ключевые компоненты:
Пример расширенной структуры:
{
version: 8,
sprite: 'mapbox://sprites/mapbox/streets-v12',
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
sources: { ... },
layers: [ ... ],
transition: {
duration: 300,
delay: 0
}
}
После инициализации карты стиль загружается асинхронно. Это означает, что доступ к слоям и источникам невозможен до завершения загрузки.
Основные события:
load — карта и стиль полностью загруженыstyle.load — стиль загружен и применёнstyledata — изменения в структуре стиляrender — обновление кадраПример корректного ожидания загрузки:
map.on('load', () => {
map.addSource('points', {
type: 'geojson',
data: '/data/points.geojson'
});
map.addLayer({
id: 'points-layer',
type: 'circle',
source: 'points'
});
});
Работа со слоями до события load приводит к ошибкам, так
как WebGL-контекст инициализируется только после применения стиля.
Mapbox GL JS позволяет менять стиль на лету:
map.setStyle('mapbox://styles/mapbox/dark-v11');
При вызове setStyle происходит полная перезагрузка графа
рендеринга: источники, слои и ресурсы пересоздаются.
Для восстановления пользовательских слоёв используется обработка события повторной загрузки:
map.on('style.load', () => {
map.addSource('custom-data', {
type: 'geojson',
data: '/data/custom.geojson'
});
map.addLayer({
id: 'custom-layer',
type: 'fill',
source: 'custom-data',
paint: {
'fill-color': '#0080ff',
'fill-opacity': 0.5
}
});
});
Одна из ключевых проблем при работе со стилями — потеря добавленных
вручную слоёв при вызове setStyle. Для решения используется
сохранение конфигурации перед сменой стиля:
const customLayers = [
{
id: 'custom-layer',
type: 'circle',
source: 'custom-source',
paint: {
'circle-radius': 5,
'circle-color': '#00ff00'
}
}
];
map.setStyle('mapbox://styles/mapbox/light-v11');
map.once('style.load', () => {
map.addSource('custom-source', {
type: 'geojson',
data: '/data/custom.geojson'
});
customLayers.forEach(layer => map.addLayer(layer));
});
При работе без Mapbox Studio стиль может храниться как локальный JSON-файл:
fetch('/styles/local-style.json')
.then(res => res.json())
.then(style => {
map.setStyle(style);
});
Это часто применяется в офлайн-приложениях или системах с динамической генерацией визуализации.
Mapbox GL JS предоставляет методы для контроля состояния:
map.isStyleLoaded() — проверка завершения загрузкиmap.getStyle() — получение текущего стиляif (map.isStyleLoaded()) {
console.log(map.getStyle());
}
Однако даже при true некоторые ресурсы (например, тайлы)
могут оставаться в процессе загрузки, поэтому критические операции
рекомендуется выполнять через события.
Для частичных изменений используется работа с API стиля:
setPaintPropertysetLayoutPropertysetFiltermap.setPaintProperty('points-layer', 'circle-color', '#ff0000');
Этот подход значительно эффективнее, чем повторный
setStyle, поскольку не разрушает текущий граф сцены.
Источники данных определяются внутри стиля и могут подключаться динамически:
map.addSource('roads', {
type: 'vector',
url: 'mapbox://mapbox.mapbox-streets-v8'
});
При использовании пользовательского стиля важно учитывать соответствие слоёв источникам: отсутствие источника приводит к пропуску слоя при рендеринге.
Браузерное кэширование играет важную роль при работе со стилями, особенно если используется CDN Mapbox. Изменения в стиле могут не отображаться мгновенно из-за кэширования:
Для обхода используются версии стилей или уникальные URL-параметры.
Порядок слоёв в массиве layers определяет их визуальный
приоритет. Последний слой рендерится поверх предыдущих:
layers: [
{ id: 'background', type: 'background' },
{ id: 'roads', type: 'line' },
{ id: 'labels', type: 'symbol' }
]
Неправильный порядок может приводить к перекрытию объектов или исчезновению элементов интерфейса.
При создании сложных стилей критично учитывать производительность WebGL:
paintИзбыточная детализация стиля напрямую влияет на FPS и время рендеринга.
Mapbox GL JS строго привязан к версии спецификации. Использование неподдерживаемых полей приводит к игнорированию части конфигурации или ошибкам рендеринга.
Рекомендуемая практика — фиксировать version: 8 и
проверять совместимость при миграции между версиями библиотек и стилевых
схем.
После загрузки стиля изменения должны выполняться с учётом асинхронности:
map.on('styledata', () => {
// безопасное изменение структуры стиля
});
Событие styledata срабатывает при любом изменении
источников или слоёв, включая внутренние операции Mapbox GL JS.
При недоступности ресурсов стиль может загружаться частично. В этом случае:
Корректная обработка ошибок предполагает мониторинг событий
error:
map.on('error', (e) => {
console.log(e.error);
});
Часто стиль используется как слой абстракции над внешними источниками данных. В этом случае структура строится вокруг динамических URL и параметризованных источников:
sources: {
dynamicData: {
type: 'geojson',
data: () => fetch('/api/data').then(r => r.json())
}
}
Такой подход позволяет строить адаптивные карты, изменяющиеся в зависимости от состояния приложения или пользовательского запроса.