MapLibre GL JS поддерживает одновременное создание множества независимых экземпляров карты на одной странице. Каждый экземпляр работает в собственном DOM-контейнере и имеет отдельный WebGL-контекст, слой состояния, набор источников данных и обработчиков событий.
Базовая схема создания нескольких карт заключается в инициализации
отдельных объектов map с разными контейнерами:
import maplibregl from "maplibre-gl";
const mapA = new maplibregl.Map({
container: "mapA",
style: "https://demotiles.maplibre.org/style.json",
center: [37.6173, 55.7558],
zoom: 10
});
const mapB = new maplibregl.Map({
container: "mapB",
style: "https://demotiles.maplibre.org/style.json",
center: [2.3522, 48.8566],
zoom: 10
});
Каждый экземпляр полностью изолирован: изменение центра, масштаба или стиля одного не влияет на другой.
Корректная организация DOM-контейнеров критична при работе с несколькими картами. Каждый контейнер должен быть заранее создан и иметь фиксированные размеры.
<div id="mapA" class="map"></div>
<div id="mapB" class="map"></div>
.map {
width: 50%;
height: 400px;
display: inline-block;
}
При динамическом создании карт важно учитывать очистку ресурсов:
mapA.remove();
mapB.remove();
Метод remove() освобождает WebGL-контекст, удаляет
обработчики событий и предотвращает утечки памяти при повторной
инициализации.
Часто используется сценарий синхронного перемещения нескольких карт: панорамирование, масштабирование и вращение одного экземпляра отражаются на других.
Основной механизм — обработка события move:
function syncMaps(source, target) {
const center = source.getCenter();
const zoom = source.getZoom();
const bearing = source.getBearing();
const pitch = source.getPitch();
target.jumpTo({ center, zoom, bearing, pitch });
}
mapA.on("move", () => syncMaps(mapA, mapB));
mapB.on("move", () => syncMaps(mapB, mapA));
Для предотвращения рекурсивных вызовов применяется флаг блокировки:
let syncing = false;
function sync(from, to) {
if (syncing) return;
syncing = true;
to.jumpTo(from.getCenter(), from.getZoom());
syncing = false;
}
mapA.on("move", () => sync(mapA, mapB));
mapB.on("move", () => sync(mapB, mapA));
При работе с несколькими картами важно учитывать частоту событий
move, которые генерируются при каждом кадре анимации.
Оптимизация достигается через привязку к более редким событиям:
moveendzoomendrotateendmapA.on("moveend", () => {
mapB.jumpTo({
center: mapA.getCenter(),
zoom: mapA.getZoom()
});
});
Такой подход снижает нагрузку на основной поток и уменьшает количество перерисовок WebGL.
Распространённый паттерн — основная карта и миниатюрный обзорный экземпляр. Мини-карта отображает глобальный контекст и синхронизируется с основной.
const mainMap = new maplibregl.Map({
container: "main",
style: styleUrl,
center: [30, 50],
zoom: 5
});
const overviewMap = new maplibregl.Map({
container: "overview",
style: styleUrl,
center: [30, 50],
zoom: 1
});
mainMap.on("move", () => {
overviewMap.jumpTo({
center: mainMap.getCenter(),
zoom: 1
});
});
Мини-карта обычно имеет отключённые интерактивные элементы:
new maplibregl.Map({
container: "overview",
style: styleUrl,
interactive: false
});
При параллельном отображении двух карт часто используется синхронизация слоя стиля или временных данных. Это позволяет сравнивать разные наборы данных или стили отображения.
const mapLeft = new maplibregl.Map({
container: "left",
style: "style-day.json"
});
const mapRight = new maplibregl.Map({
container: "right",
style: "style-night.json"
});
Синхронизация камеры:
function linkMaps(a, b) {
a.on("move", () => {
b.jumpTo({
center: a.getCenter(),
zoom: a.getZoom(),
bearing: a.getBearing()
});
});
}
linkMaps(mapLeft, mapRight);
linkMaps(mapRight, mapLeft);
Каждый экземпляр карты содержит собственный стек слоёв, источников и текстур. Добавление слоя в одну карту не влияет на другую:
mapA.on("load", () => {
mapA.addSource("points", {
type: "geojson",
data: pointsGeoJSON
});
mapA.addLayer({
id: "points-layer",
type: "circle",
source: "points"
});
});
mapB остаётся полностью независимой и требует отдельного определения источников.
Браузеры ограничивают количество одновременно активных WebGL-контекстов. При создании множества карт возможно достижение лимита, что приводит к сбросу контекста и ошибкам рендеринга.
Факторы, влияющие на стабильность:
Оптимизационные подходы включают:
remove()При интерфейсах с вкладками или модальными окнами карты часто создаются и уничтожаются динамически:
let activeMap = null;
function createMap(container) {
activeMap = new maplibregl.Map({
container,
style: styleUrl,
center: [0, 0],
zoom: 2
});
}
function destroyMap() {
if (activeMap) {
activeMap.remove();
activeMap = null;
}
}
Такой подход предотвращает накопление WebGL-контекстов и снижает нагрузку на GPU.
При наличии более двух карт целесообразно использовать централизованное состояние:
const state = {
center: [0, 0],
zoom: 3,
bearing: 0
};
function updateState(map) {
const c = map.getCenter();
state.center = [c.lng, c.lat];
state.zoom = map.getZoom();
state.bearing = map.getBearing();
}
function applyState(map) {
map.jumpTo(state);
}
Все карты читают и записывают в единый объект состояния, снижая количество прямых связей между экземплярами.
При синхронизации нескольких карт важно учитывать частоту кадров
WebGL. Использование requestAnimationFrame позволяет
сгладить обновления:
let queued = false;
mapA.on("move", () => {
if (!queued) {
queued = true;
requestAnimationFrame(() => {
mapB.jumpTo({
center: mapA.getCenter(),
zoom: mapA.getZoom()
});
queued = false;
});
}
});
Такой подход предотвращает перегрузку событийной системы и обеспечивает стабильную визуализацию при интенсивном взаимодействии.