Управление маркерами

Маркер в Mapbox GL JS представляет собой объект, привязанный к координатам на карте и отображаемый поверх слоя карты как DOM-элемент. Базовый способ создания маркера осуществляется через конструктор mapboxgl.Marker.

const marker = new mapboxgl.Marker()
  .setLngLat([37.6173, 55.7558])
  .addTo(map);

Ключевые этапы:

  • создание экземпляра маркера;
  • установка координат через setLngLat;
  • добавление на карту через addTo.

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


Настройка внешнего вида маркера

По умолчанию используется стандартный синий маркер Mapbox. Для создания кастомного отображения применяется передача DOM-элемента в конструктор.

const el = document.createElement('div');
el.className = 'custom-marker';

const marker = new mapboxgl.Marker(el)
  .setLngLat([37.6173, 55.7558])
  .addTo(map);

CSS-стили позволяют полностью контролировать визуальное представление:

.custom-marker {
  width: 30px;
  height: 30px;
  background-color: #e74c3c;
  border-radius: 50%;
  border: 2px solid white;
}

Кастомные маркеры используются для:

  • визуальной дифференциации объектов;
  • отображения категорий данных;
  • интеграции иконок и SVG-графики.

Работа с координатами маркера

Изменение позиции маркера выполняется через setLngLat, без необходимости пересоздания объекта.

marker.setLngLat([30.3141, 59.9386]);

Получение текущих координат:

const lngLat = marker.getLngLat();
console.log(lngLat.lng, lngLat.lat);

Такая модель позволяет динамически обновлять положение объектов, например:

  • при анимации;
  • при обработке потоковых данных;
  • при синхронизации с внешними API.

Удаление маркеров с карты

Удаление выполняется методом remove:

marker.remove();

После удаления маркер полностью исключается из DOM и внутреннего состояния карты.

Типичные сценарии:

  • обновление списка объектов;
  • фильтрация данных;
  • очистка слоя при смене источника данных.

Массовое управление маркерами

При работе с большим количеством объектов используется массив маркеров:

const markers = [];

data.forEach(item => {
  const marker = new mapboxgl.Marker()
    .setLngLat(item.coordinates)
    .addTo(map);

  markers.push(marker);
});

Очистка всех маркеров:

markers.forEach(marker => marker.remove());
markers.length = 0;

Такой подход обеспечивает централизованное управление жизненным циклом объектов.


Добавление всплывающих окон (Popup)

Маркер часто используется совместно с Popup, позволяя отображать дополнительную информацию.

const popup = new mapboxgl.Popup({ offset: 25 })
  .setText('Объект на карте');

const marker = new mapboxgl.Marker()
  .setLngLat([37.6173, 55.7558])
  .setPopup(popup)
  .addTo(map);

При клике на маркер всплывающее окно открывается автоматически.

Также возможно ручное управление:

popup.addTo(map);
popup.remove();

Перетаскиваемые маркеры

Маркер может быть сделан интерактивным с помощью опции draggable.

const marker = new mapboxgl.Marker({ draggable: true })
  .setLngLat([37.6173, 55.7558])
  .addTo(map);

Отслеживание изменения позиции:

marker.on('dragend', () => {
  const position = marker.getLngLat();
  console.log(position);
});

Перетаскиваемые маркеры применяются в сценариях:

  • выбора точки на карте;
  • редактирования геометрии;
  • пользовательского ввода координат.

Обработка событий маркера

Маркер поддерживает события DOM и Mapbox API. Основные события:

marker.getElement().addEventListener('click', () => {
  console.log('Клик по маркеру');
});

Также доступно использование событий карты:

map.on('click', 'layer-id', (e) => {
  console.log(e.lngLat);
});

При кастомных маркерах важно учитывать, что события привязываются к DOM-элементу, а не к объекту Marker.


Обновление маркеров без пересоздания

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

function updateMarker(marker, coordinates) {
  marker.setLngLat(coordinates);
}

Для сложных UI-элементов можно обновлять содержимое DOM:

const el = marker.getElement();
el.innerHTML = `<span>${value}</span>`;

Оптимизация большого количества маркеров

При работе с тысячами объектов стандартные маркеры становятся узким местом из-за DOM-накладных расходов.

Применяются подходы:

  • использование кластеризации через GeoJSON-источник;
  • переход на слои symbol вместо DOM-маркеров;
  • переиспользование DOM-элементов;
  • минимизация перерисовок.

Пример замены на слой:

map.addLayer({
  id: 'points',
  type: 'symbol',
  source: 'points-source',
  layout: {
    'icon-image': 'marker-15'
  }
});

Такой подход значительно повышает производительность при больших наборах данных.


Синхронизация состояния маркеров с данными

В приложениях с динамическими данными маркеры часто являются отображением состояния.

Базовый паттерн обновления:

function syncMarkers(data, markers) {
  markers.forEach(m => m.remove());
  markers.length = 0;

  data.forEach(item => {
    const marker = new mapboxgl.Marker()
      .setLngLat(item.coordinates)
      .addTo(map);

    markers.push(marker);
  });
}

Более оптимизированный подход — дифференциальное обновление:

  • добавление новых объектов;
  • обновление существующих;
  • удаление устаревших.

Использование HTML-шаблонов в маркерах

Маркер может содержать сложную структуру DOM:

const el = document.createElement('div');

el.innerHTML = `
  <div class="card-marker">
    <div class="title">Объект</div>
    <div class="status">Активен</div>
  </div>
`;

const marker = new mapboxgl.Marker(el)
  .setLngLat([37.6173, 55.7558])
  .addTo(map);

Это позволяет:

  • создавать мини-интерфейсы прямо на карте;
  • отображать статус объектов;
  • визуализировать сложные данные.

Контроль видимости маркеров

Управление отображением может выполняться через DOM или через добавление/удаление:

marker.getElement().style.display = 'none';
marker.getElement().style.display = 'block';

Альтернативный подход — условное удаление:

if (!shouldBeVisible) {
  marker.remove();
}

В системах с фильтрацией предпочтительнее второй вариант.


Группировка маркеров и логическая структура

Для удобства управления маркеры группируются по смысловым категориям:

const categories = {
  restaurants: [],
  shops: [],
  transport: []
};

Добавление в категорию:

categories.restaurants.push(marker);

Очистка категории:

categories.restaurants.forEach(m => m.remove());
categories.restaurants.length = 0;

Такая структура упрощает фильтрацию и обновление данных.


Работа с z-index и перекрытием

Порядок отображения маркеров зависит от их DOM-позиции. Для управления слоями используется CSS:

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

Или динамическое изменение:

marker.getElement().style.zIndex = 100;

Это важно при:

  • наложении маркеров;
  • выделении активного объекта;
  • реализации hover-состояний.

Переиспользование маркеров

Для повышения производительности маркеры могут переиспользоваться вместо пересоздания:

function moveMarker(marker, coords) {
  marker.setLngLat(coords);
}

Пул объектов:

const pool = [];

function getMarker() {
  return pool.pop() || new mapboxgl.Marker();
}

function releaseMarker(marker) {
  marker.remove();
  pool.push(marker);
}

Такой подход снижает нагрузку на DOM и ускоряет рендеринг при частых обновлениях.