Всплывающие окна и попапы

В интерфейсах картографических приложений всплывающие окна (popups) служат для отображения контекстной информации о географических объектах. В OpenLayers они реализуются через механизм ol.Overlay, который позволяет привязывать HTML-элементы к координатам карты и управлять их поведением независимо от слоёв.

Popups в этой библиотеке не являются отдельной сущностью «из коробки» — это композиция DOM-элемента и оверлея, синхронизированного с координатами карты.


Архитектура всплывающих окон: Overlay как основа

Ключевой механизм реализации всплывающих окон в OpenLayers — класс Overlay.

Overlay выполняет следующие функции:

  • привязка HTML-элемента к координате карты;
  • автоматическое позиционирование при перемещении карты;
  • управление смещением относительно точки привязки;
  • контроль видимости;
  • опциональное автоматическое смещение карты (autoPan).

Базовая структура popup строится вокруг контейнера:

  • внешний DOM-элемент (обёртка popup);
  • внутренний блок содержимого;
  • элемент закрытия (опционально);
  • Overlay, связывающий DOM и карту.

Создание базового popup через Overlay

В OpenLayers popup создаётся путём передачи DOM-элемента в ol.Overlay:

const container = document.createElement('div');
container.className = 'ol-popup';

const content = document.createElement('div');
content.className = 'ol-popup-content';

const closer = document.createElement('div');
closer.className = 'ol-popup-closer';
closer.innerHTML = '✕';

container.appendChild(closer);
container.appendChild(content);

const overlay = new ol.Overlay({
  element: container,
  autoPan: true,
  autoPanAnimation: {
    duration: 250
  }
});

map.addOverlay(overlay);

Ключевые параметры:

  • element — DOM-узел popup;
  • autoPan — автоматическое смещение карты при выходе popup за пределы видимости;
  • autoPanAnimation — анимация смещения.

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

Основной метод управления позиционированием — setPosition.

overlay.setPosition([lon, lat]);

В OpenLayers координаты обычно передаются в проекции карты (например, EPSG:3857), поэтому часто требуется преобразование:

const coord = ol.proj.fromLonLat([37.6173, 55.7558]);
overlay.setPosition(coord);

Скрытие popup выполняется установкой null:

overlay.setPosition(undefined);

Отображение данных объекта карты

Наиболее распространённый сценарий — отображение информации о выбранном объекте слоя.

Векторные слои в OpenLayers позволяют извлекать feature по клику через forEachFeatureAtPixel.

map.on('singleclick', function (evt) {
  const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
    return feature;
  });

  if (feature) {
    const coordinate = evt.coordinate;
    const name = feature.get('name');

    content.innerHTML = `<strong>${name}</strong>`;
    overlay.setPosition(coordinate);
  } else {
    overlay.setPosition(undefined);
  }
});

В этом сценарии popup выступает как слой представления данных feature, не нарушая архитектуру слоёв карты.


HTML-структура popup и стилизация

В OpenLayers внешний вид popup полностью определяется CSS.

Типичная структура:

<div class="ol-popup">
  <div class="ol-popup-closer"></div>
  <div class="ol-popup-content"></div>
</div>

Базовые стили:

.ol-popup {
  position: absolute;
  background-color: white;
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
  padding: 10px;
  border-radius: 8px;
  min-width: 180px;
}

.ol-popup-closer {
  position: absolute;
  top: 4px;
  right: 8px;
  cursor: pointer;
}

.ol-popup:after {
  content: '';
  position: absolute;
  bottom: -10px;
  left: 50%;
  border-width: 10px;
  border-style: solid;
  border-color: white transparent transparent transparent;
}

Визуальная «стрелка» реализуется исключительно CSS-границами без участия API карты.


Управление закрытием popup

Закрытие popup обычно связывается с очисткой позиции Overlay:

closer.oncl ick = function () {
  overlay.setPosition(undefined);
  closer.blur();
  return false;
};

В OpenLayers отсутствие позиции автоматически исключает overlay из отображения, но DOM остаётся в дереве, что позволяет повторно использовать элемент без пересоздания.


Смещение popup относительно точки привязки

Overlay поддерживает параметр offset, позволяющий сместить popup относительно координаты:

const overlay = new ol.Overlay({
  element: container,
  offset: [0, -20]
});

Типичные случаи применения:

  • компенсация высоты popup;
  • размещение над маркером;
  • предотвращение перекрытия точки клика.

AutoPan: автоматическое перемещение карты

Механизм autoPan в OpenLayers обеспечивает видимость popup, если он выходит за границы экрана.

autoPan: true,
autoPanAnimation: {
  duration: 200
}

При активации:

  • карта сдвигается так, чтобы popup полностью отображался;
  • сохраняется позиция географической точки;
  • анимация делает движение визуально плавным.

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

Подходы:

  • объединение данных features в один popup;
  • отображение списка объектов;
  • динамическое построение HTML внутри overlay.

Пример:

map.on('singleclick', function (evt) {
  const features = [];

  map.forEachFeatureAtPixel(evt.pixel, function (feature) {
    features.push(feature);
  });

  if (features.length > 0) {
    content.innerHTML = features
      .map(f => `<div>${f.get('name')}</div>`)
      .join('');

    overlay.setPosition(evt.coordinate);
  }
});

Hit detection и приоритет слоёв

В OpenLayers порядок определения объектов зависит от:

  • z-index слоя;
  • порядка добавления слоёв;
  • настроек hitTolerance;
  • типа источника данных.
map.forEachFeatureAtPixel(evt.pixel, callback, {
  hitTolerance: 5
});

hitTolerance расширяет область клика, что критично для точечных объектов.


Производительность popup-ов

Overlay работает вне WebGL/Canvas рендеринга, что делает его относительно лёгким, но при массовом использовании возникают ограничения:

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

Оптимизация достигается через:

  • переиспользование одного overlay;
  • минимизацию DOM внутри popup;
  • отказ от тяжёлых CSS-анимаций;
  • кеширование данных feature.

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

При изменении центра или масштаба карты popup автоматически пересчитывает позицию.

map.getView().animate({
  center: coord,
  zoom: 10,
  duration: 800
});

Overlay в OpenLayers синхронизируется с трансформациями view, сохраняя привязку к географической точке без дополнительного кода.


Множественные popup-ы и управление состоянием

Хотя технически возможно создавать несколько overlay, в типовых архитектурах применяется один экземпляр:

  • снижает нагрузку на DOM;
  • упрощает управление состоянием;
  • предотвращает конфликт позиционирования.

При необходимости множественных popup-ов используется массив overlay:

const overlays = [];
overlays.push(new ol.Overlay({ element: el1 }));
overlays.push(new ol.Overlay({ element: el2 }));

Каждый overlay управляется независимо, но требует синхронизации жизненного цикла.


Связь popup с кластеризацией

При использовании кластерных источников:

  • popup получает агрегированный feature;
  • данные извлекаются из feature.get('features');
  • отображается список объектов внутри кластера.
const clusterFeatures = feature.get('features');

if (clusterFeatures.length > 1) {
  content.innerHTML = clusterFeatures.length + ' объектов';
}

В OpenLayers это стандартный подход для плотных точечных наборов данных.


Управление позиционированием через pixel-to-coordinate преобразования

Popup всегда привязан к координате, но событие клика предоставляет пиксель:

const coordinate = map.getCoordinateFromPixel(evt.pixel);
overlay.setPosition(coordinate);

Этот механизм позволяет:

  • отделить UI-слой от геометрии;
  • управлять точностью позиционирования;
  • интегрировать кастомные взаимодействия.

Использование overlay в сложных интерфейсах

В более сложных сценариях OpenLayers popup становится частью UI-системы:

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

Overlay при этом используется как контейнер для произвольного HTML, а не только для отображения информации.