В MapLibre GL JS маркеры могут быть реализованы не только через слой символов (symbol layer), но и через полноценные DOM-элементы. Такой подход используется, когда требуется гибкая визуализация: сложная разметка, произвольная HTML-структура, интерактивные компоненты, формы, изображения или динамические состояния.
HTML-маркер создаётся через maplibregl.Marker, где в
качестве содержимого передаётся DOM-узел:
const el = document.createElement('div');
el.className = 'custom-marker';
el.innerHTML = `
<div class="marker-inner">
<span class="title">Объект</span>
</div>
`;
const marker = new maplibregl.Marker({
element: el
})
.setLngLat([71.4304, 51.1282])
.addTo(map);
Ключевой принцип заключается в том, что карта управляет географической привязкой, а браузер — отрисовкой DOM.
HTML-маркеры работают поверх WebGL-канваса. Каждый маркер:
transform)Позиционирование осуществляется через проекцию координат:
LngLat → Screen coordinates → CSS transform
Это означает, что при каждом перемещении карты выполняется перерасчёт экранной позиции маркера.
HTML позволяет строить сложные визуальные компоненты:
const wrapper = document.createElement('div');
wrapper.className = 'poi-marker';
const icon = document.createElement('img');
icon.src = '/icons/point.svg';
const label = document.createElement('div');
label.className = 'label';
label.textContent = 'Кафе';
wrapper.appendChild(icon);
wrapper.appendChild(label);
new maplibregl.Marker({ element: wrapper })
.setLngLat([71.45, 51.15])
.addTo(map);
Такой подход используется для:
Положение HTML-маркера относительно координаты задаётся через
anchor и offset.
const marker = new maplibregl.Marker({
element: el,
anchor: 'bottom',
offset: [0, -10]
});
Поддерживаемые якоря:
centertopbottomleftrighttop-left)Смещение позволяет точно позиционировать сложные DOM-структуры относительно географической точки, например:
HTML-маркеры сохраняют всю модель событий браузера. Это позволяет добавлять интерактивность без дополнительных абстракций:
el.addEventListener('click', (e) => {
e.stopPropagation();
console.log('Маркер нажат');
});
Типичные сценарии:
Особенно важно учитывать остановку всплытия событий, чтобы не конфликтовать с обработчиками карты.
Маркер может быть сделан draggable:
const marker = new maplibregl.Marker({
element: el,
draggable: true
})
.setLngLat([71.43, 51.12])
.addTo(map);
При перемещении происходят события:
marker.on('dragstart', () => {});
marker.on('drag', () => {});
marker.on('dragend', () => {
const lngLat = marker.getLngLat();
});
Используется для:
HTML-маркеры требуют значительных ресурсов по сравнению с WebGL-слоями. Причина — участие браузерного layout engine.
Основные ограничения:
Рекомендации по оптимизации:
transform вместо изменения
layout-свойствMapLibre GL JS автоматически синхронизирует DOM-маркеры с состоянием камеры:
Внутри используется система обновления, привязанная к render loop карты.
При масштабировании данных DOM-подход становится узким местом. Для десятков тысяч объектов HTML-маркеры не подходят.
Используются стратегии:
Пример условной замены:
if (zoom < 10) {
useClusterLayer();
} else {
useHtmlMarkers();
}
HTML-маркеры легко интегрируются с компонентной моделью UI.
Пример концепции:
useEffect(() => {
const el = document.createElement('div');
el.innerHTML = renderComponent();
const marker = new maplibregl.Marker({ element: el })
.setLngLat(coords)
.addTo(map);
return () => marker.remove();
}, [coords]);
Особенности:
CSS полностью определяет внешний вид:
.poi-marker {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
transform: translate3d(0,0,0);
}
.poi-marker .label {
font-size: 12px;
white-space: nowrap;
}
Часто применяются:
transform: translate3d для ускорения GPU-композитаwill-change: transform для оптимизации анимацийpointer-events: auto/none для управления
взаимодействиемHTML-маркеры находятся в отдельном DOM-контейнере поверх canvas. Их z-index управляется через:
z-indexЭто позволяет строить многоуровневые интерфейсы:
HTML-маркеры часто используются совместно с всплывающими окнами:
const popup = new maplibregl.Popup()
.setLngLat([71.43, 51.12])
.setHTML('<b>Объект</b>')
.addTo(map);
Связка позволяет:
При изменении масштаба карты HTML-маркеры:
Особенно заметно:
Оптимизация достигается через упрощение DOM и снижение частоты обновлений.
Каждый HTML-маркер должен быть явно удалён:
marker.remove();
Удаление включает:
При работе с динамическими наборами данных критично: