Пользовательские события

Архитектура событий в Google Maps JavaScript API построена на модели наблюдателя, где большинство объектов (карта, маркеры, оверлеи) могут как генерировать события, так и реагировать на них через подписку обработчиков. Основной механизм взаимодействия реализован через пространство google.maps.event, которое обеспечивает единый интерфейс работы с событиями независимо от типа объекта.

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


Регистрация обработчиков событий

Основной способ привязки логики к событиям — использование google.maps.event.addListener.

const map = new google.maps.Map(document.getElementById("map"), {
  center: { lat: 40.73061, lng: -73.935242 },
  zoom: 12
});

google.maps.event.addListener(map, "click", (event) => {
  console.log(event.latLng.toString());
});

Каждый обработчик получает объект события, структура которого зависит от типа источника. В случае карты это обычно объект MouseEvent, содержащий координаты latLng, позицию пикселей и дополнительные параметры.


Популярные события карты

Карты генерируют большое количество событий, наиболее часто используемые:

События мыши

  • click — одиночный клик по карте
  • dblclick — двойной клик
  • rightclick — клик правой кнопкой
  • mousemove — движение курсора
  • mousedown и mouseup — нажатие и отпускание кнопки мыши
map.addListener("click", (e) => {
  new google.maps.Marker({
    position: e.latLng,
    map: map
  });
});

События изменения состояния карты

  • center_changed — изменение центра карты
  • zoom_changed — изменение масштаба
  • bounds_changed — изменение границ видимой области
  • idle — завершение всех анимаций и загрузок тайлов
map.addListener("zoom_changed", () => {
  console.log("Zoom:", map.getZoom());
});

Событие idle играет важную роль в оптимизации: оно позволяет выполнять действия после завершения всех перерисовок, например, загрузку данных для текущей области.


События маркеров и оверлеев

Объекты типа Marker, Polyline, Polygon, Circle также поддерживают событийную модель.

Маркеры

const marker = new google.maps.Marker({
  position: { lat: 55.75, lng: 37.61 },
  map: map
});

marker.addListener("click", () => {
  console.log("Marker clicked");
});

Основные события маркеров:

  • click
  • dragstart
  • drag
  • dragend

При включённой опции draggable: true маркер становится интерактивным и генерирует события перемещения.


Полилинии и полигоны

const polygon = new google.maps.Polygon({
  paths: [
    { lat: 55.75, lng: 37.61 },
    { lat: 55.76, lng: 37.62 },
    { lat: 55.74, lng: 37.63 }
  ],
  map: map
});

polygon.addListener("click", (e) => {
  console.log(e.latLng);
});

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

  • click
  • mousemove
  • rightclick

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


Удаление обработчиков событий

При динамическом создании интерфейсов важно управлять жизненным циклом подписок. Для этого используется google.maps.event.removeListener.

const listener = map.addListener("click", () => {
  console.log("clicked");
});

google.maps.event.removeListener(listener);

Каждый вызов addListener возвращает объект слушателя, который используется для последующего удаления.


Однократные события

Для сценариев, где обработчик должен сработать только один раз, используется addListenerOnce.

map.addListenerOnce("idle", () => {
  console.log("Map fully loaded and stable");
});

Это особенно полезно при первичной инициализации интерфейса, когда повторное выполнение логики недопустимо.


Объект события

События мыши и взаимодействия обычно содержат следующие поля:

  • latLng — географические координаты
  • pixel — позиция в пикселях (в некоторых событиях)
  • domEvent — нативное DOM-событие
map.addListener("click", (e) => {
  console.log(e.latLng.lat(), e.latLng.lng());
});

Объект LatLng предоставляет методы:

  • lat()
  • lng()
  • toString()

Пользовательские события

Система событий позволяет не только слушать встроенные события, но и генерировать собственные через google.maps.event.trigger.

const marker = new google.maps.Marker({
  position: { lat: 10, lng: 10 },
  map: map
});

marker.addListener("custom_event", (data) => {
  console.log("Custom event fired:", data);
});

google.maps.event.trigger(marker, "custom_event", {
  id: 1,
  message: "Hello"
});

Механизм позволяет строить абстракции поверх стандартных объектов, расширяя их поведение без наследования.


Проброс событий между объектами

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

map.addListener("click", (e) => {
  google.maps.event.trigger(marker, "external_click", e);
});

marker.addListener("external_click", (e) => {
  console.log("Forwarded event:", e.latLng);
});

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


Контекст и замыкания в обработчиках

JavaScript-обработчики событий работают в контексте замыканий, что позволяет сохранять состояние:

function createCounterMarker(map, position) {
  let count = 0;

  const marker = new google.maps.Marker({
    position,
    map
  });

  marker.addListener("click", () => {
    count++;
    console.log("Clicks:", count);
  });

  return marker;
}

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


Производительность событийной системы

При большом количестве объектов на карте событийная система становится потенциальным источником нагрузки. Основные факторы:

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

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

  • использование idle вместо bounds_changed для тяжёлых операций
  • группировку маркеров
  • ограничение частоты обработки событий (throttling/debouncing)

Делегирование событий

При массовом количестве объектов применяется подход делегирования: обработчик назначается на контейнер (например, карту), а не на каждый элемент.

map.addListener("click", (e) => {
  if (e.placeId) {
    console.log("Place clicked");
  }
});

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


Особенности распространения событий

События в API не всегда всплывают по иерархии объектов. Например, клик по маркеру не всегда приводит к срабатыванию события карты click. Это важно учитывать при проектировании логики взаимодействия.


События редактируемых объектов

При включённом редактировании геометрии появляются дополнительные события:

  • insert_at
  • remove_at
  • set_at
const path = polygon.getPath();

path.addListener("set_at", (index) => {
  console.log("Vertex changed:", index);
});

Эти события позволяют отслеживать изменения формы объектов в реальном времени.


Синхронизация состояния через события

Событийная модель используется как основной механизм синхронизации UI и состояния карты. Любое изменение — от перемещения центра до редактирования полигона — отражается через события, что позволяет строить реактивные интерфейсы без прямого опроса состояния объектов.