Взаимодействие в Deck.gl строится поверх системы picking’а WebGL и единого обработчика событий, который агрегирует события мыши и сенсора, преобразуя их в семантические события уровня «объект сцены».
Ключевая особенность архитектуры заключается в том, что события не привязаны напрямую к DOM-элементам, как в классических интерфейсах, а вычисляются через определение попадания курсора в геометрические примитивы, отрисованные слоями.
Событийная модель включает три уровня:
Любой слой в Deck.gl по умолчанию не участвует в системе событий. Для активации требуется явное указание возможности «пикаться».
Основное свойство:
pickable: true
Без этого параметра слой игнорируется системой picking’а и не генерирует события.
Дополнительные свойства, влияющие на интерактивность:
autoHighlight: true
highlightColor: [255, 255, 0, 80]
autoHighlight включает автоматическую подсветку объекта
под курсором, а highlightColor задаёт визуальный эффект
выделения.
Компонент верхнего уровня DeckGL принимает обработчики событий, агрегирующие взаимодействие со всеми слоями.
Основные события:
<DeckGL
layers={[layer]}
onCl ick={info => {}}
onHo ver={info => {}}
onDragSt art={info => {}}
onD rag={info => {}}
onDrag End={info => {}}
/>
Каждый обработчик получает единый объект info,
содержащий контекст взаимодействия.
Объект события является центральной частью всей системы кастомных обработчиков.
Типичная структура:
{
x: 120,
y: 340,
coordinate: [37.78, -122.41],
object: { ... },
layer: LayerInstance,
sourceLayer: LayerInstance,
index: 12,
pixel: [120, 340],
color: [0, 0, 0, 255],
picked: true
}
x, y — координаты курсора в canvascoordinate — географические координаты (если
используется map projection)object — объект данных, ассоциированный с
элементомlayer — слой, инициировавший событиеindex — индекс объекта в массиве данных слояpicked — результат picking-операцииПомимо глобальных событий DeckGL, каждый слой может иметь собственные обработчики.
Пример:
new ScatterplotLayer({
id: 'points',
data,
pickable: true,
onHover: (info, event) => {
console.log(info.object);
},
onClick: (info, event) => {
console.log('clicked point', info.object);
}
});
Такая модель позволяет реализовать локальную логику взаимодействия без необходимости фильтровать события на уровне контейнера.
При наличии нескольких уровней обработчиков действует следующая последовательность:
Если событие обработано на уровне слоя и не предотвращено, оно продолжает распространяться вверх.
Deck.gl использует GPU-based picking, где каждый объект рендерится в offscreen framebuffer с уникальным цветовым идентификатором.
Алгоритм:
Это позволяет:
Расширение стандартной модели достигается через композицию событий.
Пример перехвата всех событий:
function handleInteraction(info) {
if (!info.picked) return;
const { layer, object } = info;
if (layer.id === 'points') {
processPoint(object);
}
if (layer.id === 'heatmap') {
processHeatmapCell(object);
}
}
Использование:
<DeckGL
layers={layers}
onHo ver={handleInteraction}
onCl ick={handleInteraction}
/>
Кастомные обработчики часто интегрируются с внешним состоянием (Redux, Zustand, MobX).
Типовая схема:
onHover: (info) => {
if (info.picked) {
setTooltip({
x: info.x,
y: info.y,
data: info.object
});
} else {
setTooltip(null);
}
}
Важно учитывать, что частота onHover может быть высокой,
особенно при движении мыши, что требует оптимизации обновлений
состояния.
Основная проблема кастомных обработчиков — избыточная частота вызовов.
Типовые стратегии оптимизации:
let lastCall = 0;
function onHover(info) {
const now = Date.now();
if (now - lastCall < 50) return;
lastCall = now;
handleHover(info);
}
let timeout;
function onHover(info) {
clearTimeout(timeout);
timeout = setTimeout(() => {
handleHover(info);
}, 100);
}
onHover: (info) => {
if (info.layer.id !== 'interactive-layer') return;
process(info);
}
Для сложных сценариев создаётся наследник Layer, который
переопределяет поведение interaction hooks.
import { Layer } from '@deck.gl/core';
class CustomLayer extends Layer {
getPickingInfo({ info }) {
if (info.picked) {
info.customFlag = true;
}
return info;
}
}
Такой подход позволяет внедрять дополнительную бизнес-логику на уровне инфраструктуры слоя.
Deck.gl поддерживает Pointer Events API, что обеспечивает унификацию мыши, сенсора и пера.
События:
Это важно для мобильных сценариев и устройств с несколькими источниками ввода.
Частый сценарий кастомных обработчиков — отображение tooltip.
onHover: ({ x, y, object }) => {
if (object) {
tooltip.update({
position: [x, y],
content: object.name
});
}
}
Отрисовка происходит вне WebGL-контекста, обычно через HTML overlay.
События DeckGL тесно связаны с состоянием камеры.
В info может быть доступ к координатам, уже
преобразованным в систему мира:
coordinate: [lng, lat]
При кастомной обработке важно учитывать трансформации:
Drag-события представляют отдельную категорию взаимодействий:
onDragStart: info => {},
onDrag: info => {},
onDragEnd: info => {}
Они позволяют реализовывать:
Структура info при drag сохраняет объект контекста,
обеспечивая непрерывность состояния.
Deck.gl использует controller для управления камерой
(pan/zoom/rotate).
При включённой интерактивности возможны конфликты:
Решение:
onDragStart: (info, event) => {
event.stopPropagation();
}
или через отключение контроллера:
controller: {
dragPan: false
}
В некоторых сценариях GPU picking недостаточен. Тогда применяется ручная логика:
onClick: ({ x, y }) => {
const result = customSpatialIndex.query(x, y);
process(result);
}
Это характерно для:
Deck.gl допускает асинхронную обработку:
onClick: async (info) => {
const data = await fetchDetails(info.object.id);
updateState(data);
}
Важно учитывать, что частые async вызовы могут приводить к race conditions при движении курсора.
Для сложных приложений часто вводится слой абстракции:
class EventManager {
constructor(store) {
this.store = store;
}
handleHover = (info) => {
this.store.dispatch({
type: 'HOVER_OBJECT',
payload: info.object
});
};
}
DeckGL использует этот слой как адаптер между WebGL-событиями и архитектурой приложения.
Некоторые слои позволяют переопределить поведение выбора объекта:
getPickingInfo({ info, mode }) {
if (mode === 'hover') {
return info;
}
return null;
}
Это используется для:
События Deck.gl имеют несколько ограничений:
Эти факторы критичны при проектировании кастомных обработчиков в высоконагруженных визуализациях.