Кастомизация HTML-элемента

В основе интеграции HTML с картой лежит принцип наложения DOM-слоя поверх WebGL-контекста. Карта рендерится в canvas, а HTML-элементы позиционируются относительно географических координат через проекцию LngLat → pixel. Такой подход позволяет комбинировать высокопроизводительную отрисовку карты с гибкой версткой интерфейсных компонентов.

DOM-элементы не являются частью WebGL-сцены, однако синхронизируются с картой через систему трансформаций. При каждом изменении состояния карты (pan, zoom, rotate) пересчитываются координаты всех привязанных элементов.


HTML-маркеры как базовый способ кастомизации

Наиболее распространённый механизм внедрения HTML в карту — использование Marker с опцией element.

import mapboxgl from "mapbox-gl";

const markerElement = document.createElement("div");
markerElement.className = "custom-marker";
markerElement.innerHTML = `
  <div class="pin">
    <span class="label">A</span>
  </div>
`;

new mapboxgl.Marker({
  element: markerElement,
  anchor: "bottom"
})
.setLngLat([69.2401, 41.2995])
.addTo(map);

В этом подходе ключевым является полный контроль над DOM-структурой. Любая HTML-разметка, включая вложенные элементы, SVG, формы и медиа, может быть встроена внутрь маркера.


Позиционирование и якоря

Система привязки маркеров основана на свойстве anchor, определяющем точку соприкосновения DOM-элемента с координатой.

Основные значения:

  • center
  • top
  • bottom
  • left
  • right
  • комбинированные варианты (top-left, bottom-right и т.д.)

Для точного позиционирования используется CSS-трансформация translate3d, автоматически вычисляемая библиотекой.

new mapboxgl.Marker({ anchor: "bottom" })
  .setLngLat([0, 0])
  .addTo(map);

Стилизация через CSS

HTML-маркеры полностью подчиняются CSS-каскаду. Это позволяет реализовать сложные визуальные состояния без изменения логики JavaScript.

.custom-marker {
  width: 32px;
  height: 32px;
  background: white;
  border-radius: 50%;
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
  transform: scale(1);
  transition: transform 0.2s ease;
}

.custom-marker:hover {
  transform: scale(1.2);
}

CSS-анимации работают независимо от карты, поскольку DOM-слой отделён от WebGL-рендера.


Производительность DOM-маркеров

При большом количестве HTML-элементов возникает нагрузка на:

  • layout engine браузера
  • reflow при изменении позиции карты
  • repaint при анимациях

Рекомендуется учитывать:

  • минимизацию DOM-структуры
  • отказ от тяжёлых теней и фильтров
  • использование transform вместо изменения top/left
  • кластеризацию объектов при большом объёме данных

Кастомные Popup-элементы

Popup-окна в Mapbox GL JS также основаны на HTML и позволяют строить сложные информационные панели.

new mapboxgl.Popup({ offset: 25 })
  .setLngLat([69.2401, 41.2995])
  .setHTML(`
    <div class="popup-card">
      <h3>Объект</h3>
      <p>Описание содержимого</p>
    </div>
  `)
  .addTo(map);

Popup создаёт отдельный DOM-контейнер, который управляется библиотекой и автоматически синхронизируется с координатами.


Работа с событиями DOM-элементов

HTML-маркеры сохраняют стандартную модель событий браузера. Это позволяет интегрировать взаимодействие без промежуточных абстракций.

markerElement.addEventListener("click", () => {
  console.log("Маркер активирован");
});

Особенность заключается в том, что события DOM не конфликтуют с событиями карты (click, mousemove, drag), поскольку обрабатываются в разных слоях.


Использование IControl для кастомных HTML-компонентов

Интерфейс IControl позволяет внедрять произвольные DOM-элементы в интерфейс карты (углы, панели, тулбары).

class CustomControl {
  onAdd(map) {
    this.map = map;
    this.container = document.createElement("div");
    this.container.className = "map-control";
    this.container.innerHTML = "UI";
    return this.container;
  }

  onRemove() {
    this.container.parentNode.removeChild(this.container);
    this.map = undefined;
  }
}

map.addControl(new CustomControl(), "top-right");

Контролы существуют независимо от географической привязки и фиксируются относительно viewport.


Интеграция сложных UI-библиотек

HTML-слой позволяет внедрять сторонние UI-фреймворки (React, Vue, Svelte) без изменения архитектуры карты.

Типовой подход:

  • контейнер создаётся через Marker или Control
  • внутрь монтируется приложение
  • жизненный цикл синхронизируется с картой

Особенность заключается в необходимости учитывать частоту обновления позиции, чтобы избежать лишних ререндеров.


Обновление позиции DOM-элементов

Каждый HTML-маркер связан с внутренним механизмом пересчёта координат при изменении состояния карты.

Ключевые триггеры:

  • move
  • zoom
  • rotate
  • resize

При каждом событии пересчитывается матрица проекции:

LngLat → Mercator → Screen coordinates

Ограничения HTML-слоя

Несмотря на гибкость, HTML-элементы имеют архитектурные ограничения:

  • невозможность аппаратного ускорения рендера
  • зависимость от DOM performance
  • сложность работы с десятками тысяч объектов
  • потенциальные лаги при частых обновлениях состояния

Поэтому HTML-слой используется преимущественно для интерактивных объектов, а не для массовой визуализации.


Комбинирование WebGL и DOM

В рамках Mapbox GL JS применяется гибридная модель:

  • WebGL слой — геометрия, тайлы, рельеф
  • DOM слой — интерактивные элементы, UI, маркеры

Такая архитектура позволяет разделять вычислительную нагрузку и обеспечивать независимость визуальных компонентов от графического рендеринга.


Управление z-index и слоями

DOM-элементы управляются через CSS z-index, однако их порядок дополнительно синхронизируется с внутренним порядком слоёв карты.

.custom-marker {
  position: absolute;
  z-index: 10;
}

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


Использование SVG внутри HTML-маркеров

SVG часто применяется для создания масштабируемых маркеров без потери качества при зуме.

markerElement.innerHTML = `
  <svg width="40" height="40">
    <circle cx="20" cy="20" r="10" fill="red"/>
  </svg>
`;

SVG сохраняет чёткость при любом уровне масштабирования карты, в отличие от растровых изображений.


Динамическая генерация DOM-элементов

HTML-маркеры могут формироваться на основе данных GeoJSON, где каждый объект описывает структуру интерфейса.

data.features.forEach(feature => {
  const el = document.createElement("div");
  el.className = "point";
  el.textContent = feature.properties.name;

  new mapboxgl.Marker(el)
    .setLngLat(feature.geometry.coordinates)
    .addTo(map);
});

Такой подход связывает геоданные и UI-слой напрямую, без промежуточных визуализаций.


Управление жизненным циклом DOM-объектов

При работе с динамическими наборами данных критично корректное удаление элементов:

marker.remove();

Удаление освобождает память и отключает обработчики событий, предотвращая утечки при длительной работе карты.


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

При изменении угла наклона или вращении карта сохраняет согласованность DOM-слоя через компенсационные трансформации.

HTML-элементы визуально «приклеиваются» к координатам за счёт обновления матриц преобразования в каждом кадре анимации.


Инкапсуляция сложных интерфейсных компонентов

HTML-маркеры могут содержать полноценные интерфейсы:

  • формы ввода
  • графики
  • кнопки управления
  • медиа-контент

При этом сохраняется независимость логики компонента от географической системы координат, что позволяет строить интерактивные GIS-интерфейсы поверх карты без модификации WebGL-слоя.