Система событий в Vega основана на модели реактивных сигналов и потоков пользовательского ввода, где любое действие мыши или клавиатуры превращается в событие, которое может быть обработано декларативно внутри спецификации визуализации. В отличие от императивных графических библиотек, обработчики не привязываются к DOM напрямую — вместо этого события описываются через выражения и связываются с сигналами, состоянием и трансформациями данных.
Событийная модель опирается на несколько уровней абстракции: view events, item events, signal events, а также интеграцию с selection-механизмами Vega-Lite. Это позволяет строить сложные интерактивные визуализации без ручного управления DOM-слушателями.
Каждое взаимодействие пользователя в Vega интерпретируется как поток событий:
В Vega эти события не обрабатываются напрямую, а используются в event streams, которые привязываются к сигналам:
{
"signals": [
{
"name": "cursor",
"on": [
{
"events": "view:mousemove",
"update": "xy()"
}
]
}
]
}
Здесь view:mousemove — поток событий от всей сцены
визуализации, а xy() — функция, возвращающая координаты
курсора.
Мышь является основным источником интерактивности. Vega различает события:
view:clickview:mousemoveview:mouseoverview:mouseoutview:mousedownview:mouseupview:wheelКаждое событие может быть привязано к сигналу:
{
"signals": [
{
"name": "lastClick",
"on": [
{
"events": "view:click",
"update": "datum"
}
]
}
]
}
Здесь datum — данные элемента, по которому произошёл
клик. Если клик произошёл по фону, datum будет
null.
Vega позволяет различать события не только по сцене, но и по конкретным графическим примитивам:
rect:clicksymbol:mouseovertext:mouseoutПример:
{
"signals": [
{
"name": "hoveredPoint",
"on": [
{
"events": "symbol:mouseover",
"update": "datum.id"
},
{
"events": "symbol:mouseout",
"update": "null"
}
]
}
]
}
Такая модель позволяет строить интерактивные графики с подсветкой точек без императивного кода.
События можно фильтровать с помощью выражений:
{
"events": {
"source": "view",
"type": "click",
"filter": "event.shiftKey === true"
}
}
Это позволяет обрабатывать, например, только клики с зажатым Shift.
В выражениях доступны:
event — нативное DOM-событиеitem — визуальный элементdatum — данные элементаxy() — координаты курсораКлавиатура обрабатывается через глобальные события окна или представления:
window:keydownwindow:keyupview:keydown{
"signals": [
{
"name": "pressedKey",
"on": [
{
"events": "window:keydown",
"update": "event.key"
}
]
}
]
}
Это позволяет отслеживать нажатые клавиши и строить горячие клавиши для управления визуализацией.
Можно реагировать на сочетания:
{
"signals": [
{
"name": "resetZoom",
"on": [
{
"events": {
"source": "window",
"type": "keydown",
"filter": "event.key === 'r' && event.ctrlKey"
},
"update": "true"
}
]
}
]
}
Vega-Lite абстрагирует низкоуровневые события через механизм selection. Вместо прямого описания event streams используется декларация взаимодействий.
{
"selection": {
"highlight": {
"type": "point",
"on": "click"
}
}
}
Здесь:
type: "point" — одиночный выборon: "click" — событие активации{
"selection": {
"hover": {
"type": "single",
"on": "mouseover",
"empty": "none"
}
}
}
Такой механизм часто используется для подсветки элементов без клика.
{
"selection": {
"brush": {
"type": "interval",
"on": "[mousedown, mouseup]"
}
}
}
Это создаёт прямоугольное выделение области с помощью мыши.
События в Vega и Vega-Lite всегда связаны с данными через
datum. Это ключевой механизм реактивной визуализации.
Пример реакции на клик:
{
"signals": [
{
"name": "selectedValue",
"on": [
{
"events": "rect:click",
"update": "datum.value"
}
]
}
]
}
Здесь событие напрямую изменяет состояние визуализации.
Интерактивные жесты строятся как комбинации базовых событий.
{
"signals": [
{
"name": "dragX",
"on": [
{
"events": "symbol:mousedown",
"update": "x()"
},
{
"events": "window:mousemove",
"update": "x()"
}
]
}
]
}
{
"signals": [
{
"name": "zoom",
"on": [
{
"events": "view:wheel",
"update": "pow(1.001, -event.deltaY)"
}
]
}
]
}
Каждое событие привязано к источнику:
Пример различия:
"events": "item:click"
и
"events": "view:click"
Первое срабатывает только по элементу, второе — по любому месту сцены.
При одновременном срабатывании нескольких событий применяется порядок:
Также можно контролировать распространение через фильтрацию:
"filter": "event.defaultPrevented === false"
Система Vega оптимизирована для большого количества событий за счёт:
Особенно важно при работе с mousemove и
wheel, где поток событий может быть очень интенсивным.
Интерактивные сценарии часто используют оба типа ввода одновременно.
Пример: перемещение объекта с зажатым Shift:
{
"signals": [
{
"name": "moveEnabled",
"on": [
{
"events": {
"source": "window",
"type": "keydown",
"filter": "event.shiftKey"
},
"update": "true"
},
{
"events": {
"source": "window",
"type": "keyup",
"filter": "!event.shiftKey"
},
"update": "false"
}
]
}
]
}
Vega использует выражения, похожие на JavaScript, но ограниченные безопасным подмножеством:
event, datum,
itemx(), y(),
xy())Пример:
"update": "clamp(x(), 0, width)"
Это позволяет ограничивать движение курсора в пределах графика.
События напрямую управляют состоянием визуализации:
Пример подсветки при hover:
{
"encode": {
"enter": {
"fill": {"value": "steelblue"}
},
"hover": {
"fill": {"value": "orange"}
}
}
}
Хотя это не прямое событие, оно логически связано с
symbol:mouseover.
В Vega можно строить цепочки реакций:
Такие цепочки формируют полноценную интерактивную модель интерфейса, где каждый ввод пользователя становится частью реактивного графа вычислений.