Обработка кликов и наведений

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

Интерактивность активируется на уровне слоя через свойство pickable. Без его включения слой не участвует в обработке событий, даже если он визуально присутствует на сцене. При включённом pickable Deck.gl отслеживает попадание указателя в геометрию слоя и формирует объект info, передаваемый в обработчики.


Система взаимодействия опирается на два основных события:

  • наведение курсора (onHover)
  • клик (onClick)

Оба события получают единый объект с информацией о picking:

const layer = new ScatterplotLayer({
  id: 'points',
  data,
  pickable: true,

  onHover: info => {
    console.log(info);
  },

  onClick: info => {
    console.log(info);
  }
});

Структура объекта info содержит ключевые поля:

  • x, y — экранные координаты курсора
  • coordinate — географические координаты (если применимо)
  • object — объект данных, связанный с пиксельным попаданием
  • index — индекс элемента в массиве данных
  • layer — ссылка на слой, сгенерировавший событие
  • viewport — текущее состояние камеры

Поле object является центральным элементом: именно оно связывает визуальный пиксель с исходными данными.


Включение и настройка picking

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

new Deck({
  initialViewState,
  controller: true,
  layers: [
    new ScatterplotLayer({
      id: 'points',
      data,
      pickable: true
    })
  ]
});

Ключевые параметры, влияющие на поведение picking:

  • pickable — разрешает участие слоя в событиях
  • autoHighlight — включает автоматическое подсвечивание объекта при наведении
  • highlightColor — цвет подсветки
  • highlightedObjectIndex — ручное управление выделением

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


Объект события и его структура

Объект info, передаваемый в обработчики, имеет детализированную структуру:

{
  x: 320,
  y: 180,
  coordinate: [73.56, 45.12],
  object: { id: 1, value: 42 },
  index: 10,
  layer: ScatterplotLayer,
  viewport: WebMercatorViewport,
  picked: true
}

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


Наведение и частота событий

Событие onHover вызывается при каждом движении курсора. Это требует контроля частоты обновлений, особенно при сложных слоях и больших наборах данных.

Типичная проблема — избыточные перерендеры интерфейса при каждом пиксельном смещении. Для её решения применяется мемоизация и фильтрация:

onHover: info => {
  if (info.picked) {
    updateTooltip(info.object);
  } else {
    hideTooltip();
  }
}

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

let lastObject = null;

onHover: info => {
  if (info.object !== lastObject) {
    lastObject = info.object;
    updateTooltip(info.object);
  }
}

Обработка кликов и выбор объектов

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

onClick: info => {
  if (!info.picked) {
    return;
  }

  selectFeature(info.object);
}

В сложных сценах возможна конкуренция слоёв за обработку клика. Deck.gl выбирает верхний визуально доступный объект, основываясь на порядке слоёв и их прозрачности.


Управление радиусом подбора (pickingRadius)

При работе с мелкими объектами точечный клик может быть недостаточным. Для расширения области захвата используется pickingRadius:

new Deck({
  controller: true,
  pickingRadius: 5,
  layers: [
    new ScatterplotLayer({
      id: 'points',
      data,
      pickable: true
    })
  ]
});

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


Tooltip и динамическое отображение информации

Deck.gl предоставляет встроенную поддержку подсказок через getTooltip:

new Deck({
  layers: [
    new ScatterplotLayer({
      id: 'points',
      data,
      pickable: true
    })
  ],

  getTooltip: ({object}) =>
    object && {
      html: `<div>${object.name}</div>`,
      style: {
        backgroundColor: '#fff',
        color: '#000'
      }
    }
});

Tooltip формируется на основе текущего info и автоматически обновляется при движении курсора.


Координаты и преобразование контекста

Поле coordinate в событиях зависит от системы координат слоя. В географических слоях это обычно [longitude, latitude], в экранных — пиксельные координаты.

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


Обработка событий в React-обёртке

В @deck.gl/react события передаются через свойства компонента DeckGL:

import DeckGL from '@deck.gl/react';

<DeckGL
  initialViewState={viewState}
  controller
  layers={layers}
  onCl ick={handleClick}
  onHo ver={handleHover}
/>

В этом случае события агрегируются на уровне всей сцены, а не отдельного слоя. Для определения источника используется поле layer внутри info.


Конкуренция слоёв и порядок обработки

При перекрытии слоёв важную роль играет порядок их объявления. Верхний слой имеет приоритет при picking. Однако прозрачность и visible также влияют на результат.

Для сложных сцен применяется стратегия явного управления интерактивностью:

  • отключение pickable у фоновых слоёв
  • использование visible: false для временного исключения
  • разделение интерактивных и декоративных слоёв

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

Обработка событий напрямую влияет на FPS при интенсивном движении курсора. Основные факторы нагрузки:

  • сложные геометрические слои (ArcLayer, PathLayer)
  • большое количество объектов
  • частые обновления состояния UI

Оптимизация включает:

  • кэширование результата обработки info
  • ограничение частоты обновлений (throttling)
  • минимизацию работы внутри onHover
let lastTime = 0;

onHover: info => {
  const now = performance.now();
  if (now - lastTime < 16) return;

  lastTime = now;
  processHover(info);
}

Синтетические события и кастомная логика

Deck.gl позволяет строить поверх стандартного picking собственные механизмы взаимодействия. Например, группировка объектов или кастомный hit-test на уровне данных:

onClick: info => {
  const cluster = findCluster(info.object);
  expandCluster(cluster);
}

В таких сценариях info.object становится входной точкой для дополнительной бизнес-логики, а не конечным результатом выбора.