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

Работа с интерактивными элементами на карте в JavaScript-реализации HERE Maps строится вокруг управления состоянием UI-объектов: информационных окон, балунов, всплывающих карточек, кастомных оверлеев и DOM-слоёв, привязанных к географическим координатам. Центральной задачей становится контроль их открытия, закрытия, переключения и синхронизации с событиями карты.

Базовая модель состояния всплывающих элементов

В архитектуре HERE Maps UI каждый всплывающий элемент рассматривается как объект с жизненным циклом:

  • создание (инициализация и привязка к карте)
  • открытие (визуализация и добавление в сцену)
  • активное состояние (взаимодействие)
  • закрытие (удаление из сцены или скрытие)

Ключевое различие между типами элементов заключается в том, управляются ли они через H.ui.UI (UI-слой) или через H.map (низкоуровневые объекты карты).


Информационные окна (InfoBubble) и их управление

Наиболее распространённый механизм всплывающих окон — H.ui.InfoBubble. Он используется для отображения информации, связанной с координатой или объектом карты.

Создание и открытие InfoBubble

const platform = new H.service.Platform({
  apikey: 'API_KEY'
});

const defaultLayers = platform.createDefaultLayers();

const map = new H.Map(
  document.getElementById('map'),
  defaultLayers.vector.normal.map,
  {
    zoom: 10,
    center: { lat: 52.52, lng: 13.405 }
  }
);

const ui = H.ui.UI.createDefault(map, defaultLayers);

const bubble = new H.ui.InfoBubble(
  { lat: 52.52, lng: 13.405 },
  {
    content: '<div>Точка интереса</div>'
  }
);

ui.addBubble(bubble);

Добавление объекта в UI автоматически переводит его в состояние «открыт».


Закрытие InfoBubble

Удаление всплывающего окна выполняется через UI-контейнер:

ui.removeBubble(bubble);

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


Переключение состояния (toggle behavior)

Типичная задача — обеспечение поведения «один открыт, остальные закрыты». Для этого требуется хранить ссылку на активное окно.

let activeBubble = null;

function openBubble(position, html) {
  if (activeBubble) {
    ui.removeBubble(activeBubble);
    activeBubble = null;
  }

  const bubble = new H.ui.InfoBubble(position, {
    content: html
  });

  ui.addBubble(bubble);
  activeBubble = bubble;
}

Данная модель исключает накопление открытых окон и снижает нагрузку на UI-слой.


Закрытие по клику на карту

Закрытие всех активных всплывающих элементов часто привязывается к событию tap у карты:

map.addEventListener('tap', () => {
  if (activeBubble) {
    ui.removeBubble(activeBubble);
    activeBubble = null;
  }
});

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


Привязка всплывающих окон к маркерам

Маркер (H.map.Marker) часто используется как триггер открытия информационного окна.

const marker = new H.map.Marker({ lat: 52.52, lng: 13.405 });

map.addObject(marker);

marker.addEventListener('tap', (evt) => {
  const position = evt.target.getGeometry();

  if (activeBubble) {
    ui.removeBubble(activeBubble);
  }

  activeBubble = new H.ui.InfoBubble(position, {
    content: '<div>Информация о маркере</div>'
  });

  ui.addBubble(activeBubble);
});

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


Множественные всплывающие окна и их ограничения

При работе с несколькими окнами необходимо учитывать поведение UI-слоя:

  • одновременное отображение большого количества bubble приводит к перекрытию
  • порядок отображения определяется z-index внутри UI-контейнера
  • производительность снижается при превышении десятков активных окон

Для контроля используется массив активных объектов:

const bubbles = [];

function addBubble(position, content) {
  const bubble = new H.ui.InfoBubble(position, { content });

  ui.addBubble(bubble);
  bubbles.push(bubble);
}

function closeAllBubbles() {
  bubbles.forEach(b => ui.removeBubble(b));
  bubbles.length = 0;
}

Обновление содержимого без закрытия

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

activeBubble.setContent('<div>Обновлённые данные</div>');

Такой подход сохраняет позицию и состояние окна, минимизируя перерисовку UI.


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

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

activeBubble.setPosition({ lat: 52.53, lng: 13.41 });

Это особенно важно при работе с движущимися объектами.


Автоматическое закрытие при изменении масштаба

Часто требуется скрывать всплывающие окна при zoom-событиях:

map.addEventListener('mapviewchange', () => {
  if (activeBubble) {
    ui.removeBubble(activeBubble);
    activeBubble = null;
  }
});

Такое поведение снижает визуальный шум при смене уровня детализации карты.


Интеграция с кастомными DOM-оверлеями

Помимо InfoBubble, применяются пользовательские HTML-слои через H.map.DomMarker:

const domIcon = document.createElement('div');
domIcon.innerHTML = '<div class="popup">Контент</div>';

const domMarker = new H.map.DomMarker(
  { lat: 52.52, lng: 13.405 },
  { icon: new H.map.DomIcon(domIcon) }
);

map.addObject(domMarker);

Управление открытием и закрытием в этом случае реализуется вручную через изменение DOM:

domIcon.style.display = 'none'; // закрытие
domIcon.style.display = 'block'; // открытие

Контроль групп всплывающих элементов

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

const group = new H.map.Group();

group.addEventListener('tap', (evt) => {
  const target = evt.target;

  if (target instanceof H.map.Marker) {
    openBubble(target.getGeometry(), 'Групповой элемент');
  }
});

map.addObject(group);

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


Синхронизация состояния UI и карты

Критическим аспектом становится согласование состояния всплывающих окон с состоянием карты:

  • изменение центра карты может требовать закрытия окон
  • изменение слоя (layer switch) может инвалидировать привязки
  • пересоздание карты требует очистки UI-объектов
function resetUI() {
  if (activeBubble) {
    ui.removeBubble(activeBubble);
    activeBubble = null;
  }
}

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

Повторное открытие окна на той же координате требует либо пересоздания объекта, либо повторной активации:

if (activeBubble && activeBubble.getPosition().equals(position)) {
  ui.removeBubble(activeBubble);
  activeBubble = null;
} else {
  openBubble(position, content);
}

Такой подход предотвращает дублирование окон.


Практика управления состоянием через контроллер

При усложнении логики вводится отдельный контроллер управления всплывающими элементами:

class BubbleController {
  constructor(ui) {
    this.ui = ui;
    this.current = null;
  }

  open(position, content) {
    if (this.current) {
      this.ui.removeBubble(this.current);
    }

    this.current = new H.ui.InfoBubble(position, { content });
    this.ui.addBubble(this.current);
  }

  close() {
    if (this.current) {
      this.ui.removeBubble(this.current);
      this.current = null;
    }
  }
}

Такой слой абстракции упрощает масштабирование логики UI и уменьшает связанность компонентов.


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

При удалении или пересоздании карты требуется явная очистка всех UI-объектов:

function disposeMap() {
  if (activeBubble) {
    ui.removeBubble(activeBubble);
    activeBubble = null;
  }

  map.dispose();
}

Игнорирование этого этапа приводит к утечкам памяти и неконсистентному состоянию интерфейса.


Сводная модель управления состояниями

Поведение системы всплывающих окон в HERE Maps сводится к следующим правилам:

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

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