Базовая интеграция MapLibre GL JS в Svelte начинается с установки зависимостей и подготовки контейнера для WebGL-карты.
npm install maplibre-gl
Дополнительно требуется CSS-бандл библиотеки, обеспечивающий корректное отображение элементов карты:
import 'maplibre-gl/dist/maplibre-gl.css';
В типичном Svelte-проекте (Vite-based) структура компонентов позволяет изолировать карту в отдельный модуль, что упрощает управление жизненным циклом WebGL-контекста и предотвращает утечки ресурсов.
Создание карты в Svelte строится вокруг onMount,
поскольку доступ к DOM и WebGL-контексту возможен только на клиенте.
<script>
import { onMount, onDestroy } from 'svelte';
import maplibregl from 'maplibre-gl';
import 'maplibre-gl/dist/maplibre-gl.css';
let mapContainer;
let map;
onMount(() => {
map = new maplibregl.Map({
container: mapContainer,
style: 'https://demotiles.maplibre.org/style.json',
center: [37.6173, 55.7558],
zoom: 10
});
map.addControl(new maplibregl.NavigationControl());
return () => {
map.remove();
};
});
</script>
<div bind:this={mapContainer} class="map"></div>
<style>
.map {
width: 100%;
height: 100vh;
}
</style>
Ключевым элементом является bind:this, обеспечивающий
передачу DOM-узла в MapLibre как контейнера рендеринга.
MapLibre GL JS создаёт WebGL-контекст, который требует явного освобождения. В Svelte это связывается с фазой уничтожения компонента.
Основные аспекты управления:
onMountmap.remove()Типичная ошибка — повторное создание карты без очистки предыдущего экземпляра, что приводит к утечкам памяти и блокировке GPU-ресурсов.
Svelte-реактивность позволяет связывать параметры карты с переменными
состояния. MapLibre не является реактивным по своей природе, поэтому
синхронизация выполняется вручную через set-методы API.
<script>
export let center = [0, 0];
let map;
$: if (map && center) {
map.setCenter(center);
}
</script>
Изменение center приводит к вызову
setCenter, синхронизирующему визуальное состояние
карты.
<script>
export let zoom = 5;
export let bearing = 0;
export let pitch = 0;
$: if (map) {
map.setZoom(zoom);
map.setBearing(bearing);
map.setPitch(pitch);
}
</script>
При частых изменениях параметров целесообразно применять debounce-механизмы, чтобы снизить нагрузку на WebGL-поток.
MapLibre генерирует события, отражающие изменения камеры. Эти события могут синхронизироваться с состоянием Svelte.
map.on('move', () => {
const center = map.getCenter();
const zoom = map.getZoom();
// обновление внешнего состояния
});
Для предотвращения циклических обновлений используется флаг блокировки:
let internalUpdate = false;
map.on('move', () => {
if (internalUpdate) return;
});
MapLibre GL JS опирается на модель источников данных и слоёв
визуализации. В Svelte их добавление обычно выполняется после события
load.
map.on('load', () => {
map.addSource('points', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: []
}
});
map.addLayer({
id: 'points-layer',
type: 'circle',
source: 'points',
paint: {
'circle-radius': 6,
'circle-color': '#3b82f6'
}
});
});
Обновление данных источника выполняется без пересоздания слоя:
map.getSource('points').setData(newGeojson);
Svelte stores обеспечивают централизованное состояние для геоданных и параметров карты.
import { writable } from 'svelte/store';
export const geoData = writable({
type: 'FeatureCollection',
features: []
});
Подписка внутри компонента:
<script>
import { geoData } from './stores.js';
let map;
geoData.subscribe((data) => {
if (map && map.getSource('points')) {
map.getSource('points').setData(data);
}
});
</script>
Такой подход отделяет визуализацию от логики данных и облегчает масштабирование приложения.
MapLibre позволяет динамически менять стиль карты. В Svelte это часто связано с реактивными переменными.
<script>
export let styleUrl;
$: if (map && styleUrl) {
map.setStyle(styleUrl);
}
</script>
После смены стиля требуется повторное добавление источников и слоёв, так как они не сохраняются при полной перезагрузке style JSON.
map.on('styledata', () => {
// восстановление слоёв
});
MapLibre поддерживает HTML-маркеры через Marker, что
удобно в Svelte благодаря возможности рендеринга компонентов в DOM.
const markerNode = document.createElement('div');
markerNode.className = 'marker';
new maplibregl.Marker(markerNode)
.setLngLat([37.6173, 55.7558])
.addTo(map);
Svelte-компоненты могут быть смонтированы внутрь маркера через
new Component({ target }), создавая полностью кастомные
интерактивные элементы.
События MapLibre интегрируются с Svelte через стандартную модель подписок:
clickmousemovezoomrotatedragmap.on('click', (e) => {
const coordinates = e.lngLat;
});
Для сложных интерфейсов используется разделение логики событий и состояния через stores.
При работе с MapLibre GL JS внутри Svelte критичны следующие аспекты:
Частые изменения состояния без контроля приводят к перегрузке WebGL:
setDataGeoJSON-объекты больших размеров целесообразно мемоизировать.
Повторная передача идентичных объектов в setData может
вызывать ненужный пересчёт рендеринга.
MapLibre использует GPU-ускоренные анимации. При интеграции с Svelte
важно избегать конкурирующих таймеров
(requestAnimationFrame вне MapLibre), чтобы не создавать
конфликт циклов отрисовки.
MapLibre GL JS зависит от WebGL и window, поэтому в
SSR-окружениях требуется изоляция клиентского кода.
<script>
import { browser } from '$app/environment';
if (browser) {
import('maplibre-gl').then((module) => {
// инициализация
});
}
</script>
Компонент карты размещается только на клиентской стороне, предотвращая ошибки гидратации.
Размер контейнера карты должен синхронизироваться с layout Svelte-приложения.
new ResizeObserver(() => {
map.resize();
}).observe(mapContainer);
Это обеспечивает корректную перерисовку при изменении размеров flex/grid контейнеров.
MapLibre позволяет добавлять кастомные контролы через интерфейс
IControl.
class CustomControl {
onAdd(map) {
this._map = map;
this._container = document.createElement('div');
this._container.className = 'custom-control';
return this._container;
}
onRemove() {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
}
}
map.addControl(new CustomControl(), 'top-right');
В Svelte такие контролы часто синхронизируются с store-состоянием интерфейса.
При наличии нескольких экземпляров MapLibre в одном приложении требуется строгая изоляция состояния:
Пересечение source id между картами приводит к
конфликтам глобального registry MapLibre.
MapLibre поддерживает vector tiles через vector
source:
map.addSource('tiles', {
type: 'vector',
url: 'pmtiles://endpoint'
});
В Svelte это часто комбинируется с динамической сменой источников в зависимости от состояния приложения, например выбора слоя данных или темы отображения.
WebGL может быть недоступен на некоторых устройствах или в ограниченных окружениях. Проверка выполняется через:
maplibregl.supported();
При отсутствии поддержки рендеринг может быть заменён статическим изображением или упрощённым DOM-слоем, управляемым Svelte.
Типовая структура приложения с MapLibre и Svelte:
Такое разделение снижает связанность между UI и картографическим движком и упрощает масштабирование сложных гео-приложений.