Popup элементы

Mapbox GL JS предоставляет класс Popup как базовый механизм отображения всплывающих информационных окон поверх карты. Popup является самостоятельным DOM-элементом, привязанным к координатам карты или к объектам (например, маркерам), и используется для вывода контекстной информации: описаний объектов, данных API, пользовательских подсказок и динамического контента.

Popup создаётся через конструктор mapboxgl.Popup, где задаются параметры поведения и внешнего вида.

const popup = new mapboxgl.Popup();

Базовая конфигурация позволяет управлять ключевыми аспектами отображения:

  • closeButton — отображение кнопки закрытия
  • closeOnClick — закрытие при клике по карте
  • anchor — позиция “якоря” относительно точки
  • offset — смещение относительно координаты
  • className — пользовательский CSS-класс

Пример с настройкой поведения:

const popup = new mapboxgl.Popup({
  closeButton: false,
  closeOnClick: true,
  anchor: 'top',
  offset: 10
});

Привязка Popup к координатам

Popup не отображается сам по себе — он должен быть привязан к географической точке через setLngLat.

popup
  .setLngLat([69.589, 42.874])
  .setHTML('<h3>Объект</h3><p>Описание точки</p>')
  .addTo(map);

Метод addTo(map) добавляет popup в контекст карты и делает его видимым.

Методы установки содержимого

Popup поддерживает несколько способов задания содержимого.

HTML-контент

popup.setHTML(`
  <div>
    <strong>Название объекта</strong>
    <p>Краткое описание</p>
  </div>
`);

DOM-элемент

const el = document.createElement('div');
el.innerHTML = '<p>Содержимое через DOM</p>';

popup.setDOMContent(el);

Использование setDOMContent позволяет интегрировать сложную логику: формы, кнопки, обработчики событий.

Очистка содержимого

popup.setDOMContent(document.createElement('div'));

или обновление через пустой HTML:

popup.setHTML('');

Привязка Popup к маркерам

Наиболее распространённый сценарий — привязка всплывающего окна к маркеру.

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

const popup = new mapboxgl.Popup({ offset: 25 })
  .setHTML('Информация о маркере');

marker.setPopup(popup);

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

Поведение при взаимодействии с картой

Popup управляет своим жизненным циклом в зависимости от настроек:

  • closeOnClick: true — закрытие при клике вне popup
  • closeOnClick: false — остаётся открытым
  • автоматическое позиционирование при перемещении карты

Пример:

const popup = new mapboxgl.Popup({
  closeOnClick: false
})
  .setLngLat([69.589, 42.874])
  .setHTML('Закрывается только вручную')
  .addTo(map);

Якоря (anchor) и позиционирование

Anchor определяет, с какой стороны точки привязывается popup.

Доступные значения:

  • top
  • bottom
  • left
  • right
  • top-left
  • top-right
  • bottom-left
  • bottom-right
const popup = new mapboxgl.Popup({
  anchor: 'bottom'
});

Popup автоматически корректирует позицию, если выходит за границы viewport.

Смещение (offset)

Offset позволяет точно настроить расположение относительно точки.

const popup = new mapboxgl.Popup({
  offset: [0, -20]
});

Также поддерживаются объектные конфигурации для разных якорей:

const popup = new mapboxgl.Popup({
  offset: {
    top: [0, 10],
    bottom: [0, -10],
    left: [10, 0],
    right: [-10, 0]
  }
});

Динамическое обновление содержимого

Popup можно обновлять без пересоздания:

popup.setHTML('<p>Загрузка...</p>');

fetch('/api/info')
  .then(res => res.json())
  .then(data => {
    popup.setHTML(`<p>${data.name}</p>`);
  });

Работа с событиями

Popup реагирует на изменения состояния:

popup.on('open', () => {
  console.log('Popup открыт');
});

popup.on('close', () => {
  console.log('Popup закрыт');
});

Также можно отслеживать изменения карты, чтобы синхронизировать данные.

Несколько popup на карте

Одновременно может существовать несколько popup, но управление ими требует контроля состояния:

const popup1 = new mapboxgl.Popup().setLngLat([69.5, 42.8]).setHTML('A');
const popup2 = new mapboxgl.Popup().setLngLat([69.6, 42.9]).setHTML('B');

popup1.addTo(map);
popup2.addTo(map);

Для предотвращения наложений часто применяется стратегия закрытия предыдущего popup перед открытием нового.

Стилизация Popup

Popup имеет внутреннюю структуру DOM:

  • .mapboxgl-popup
  • .mapboxgl-popup-content
  • .mapboxgl-popup-close-button
  • .mapboxgl-popup-tip

Кастомизация через CSS:

.mapboxgl-popup-content {
  background: #1e1e1e;
  color: white;
  border-radius: 8px;
  padding: 12px;
}

.mapboxgl-popup-tip {
  border-top-color: #1e1e1e;
}

С помощью className можно задавать уникальные стили:

const popup = new mapboxgl.Popup({
  className: 'custom-popup'
});

TrackPointer и привязка к курсору

Popup может следовать за курсором мыши:

const popup = new mapboxgl.Popup({
  closeButton: false,
  closeOnClick: false,
  trackPointer: true
})
  .setHTML('Данные под курсором')
  .addTo(map);

Этот режим полезен для интерактивной аналитики слоёв.

Производительность и оптимизация

При массовом использовании popup важно учитывать:

  • избыток DOM-элементов снижает FPS
  • частое обновление содержимого вызывает перерасчёт layout
  • лучше переиспользовать один popup вместо создания множества

Оптимизированный подход:

const popup = new mapboxgl.Popup();

map.on('mousemove', (e) => {
  popup
    .setLngLat(e.lngLat)
    .setHTML(`x: ${e.point.x}, y: ${e.point.y}`)
    .addTo(map);
});

Интеграция с внешними данными

Popup часто используется как слой отображения данных API:

map.on('click', (e) => {
  fetch(`/api/feature?lng=${e.lngLat.lng}&lat=${e.lngLat.lat}`)
    .then(res => res.json())
    .then(data => {
      new mapboxgl.Popup()
        .setLngLat(e.lngLat)
        .setHTML(`<strong>${data.title}</strong>`)
        .addTo(map);
    });
});

Управление жизненным циклом

Popup поддерживает явное удаление:

popup.remove();

Это освобождает DOM и предотвращает утечки памяти при динамических интерфейсах.

Поведение при анимации карты

Popup автоматически синхронизируется с:

  • zoom
  • rotation
  • pitch

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