Обработчики событий: поле on

Модель событий в Vega

Система визуализации Vega построена вокруг декларативного описания интерактивности через поток событий. В отличие от императивных подходов (где обработчики навешиваются через addEventListener), здесь взаимодействие описывается как часть спецификации. Центральный механизм — сигналы (signals), а поле on определяет, какие события из DOM или графических примитивов преобразуются в изменения состояния.

Событийная модель Vega основана на потоках событий (event streams), которые можно фильтровать, комбинировать и преобразовывать. Поле on задаёт набор правил:

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

Поле on в сигналах Vega

Сигнал в Vega описывает реактивное значение, которое может изменяться под воздействием событий. Поле on является массивом обработчиков событий.

Структура:

{
  "signals": [
    {
      "name": "exampleSignal",
      "value": 0,
      "on": [
        {
          "events": "...",
          "update": "..."
        }
      ]
    }
  ]
}

Ключевые свойства обработчика

Каждый объект внутри on содержит:

  • events — описание источника событий
  • update — выражение, вычисляющее новое значение сигнала
  • (опционально) force — принудительное обновление даже без изменения значения
  • (в некоторых случаях) between, consume — управление распространением событий

Синтаксис events

Поле events задаёт поток событий. Оно может быть:

1. Простым DOM-событием

{
  "events": "mousedown"
}

2. С привязкой к элементу

{
  "events": "rect:mouseover"
}

Здесь событие ограничено графическими примитивами Vega (rect, symbol, text и т.д.).

3. С фильтрацией по модификаторам

{
  "events": "mousedown[event.shiftKey]"
}

Используется JavaScript-подобная фильтрация через выражения.

4. Последовательности событий (event streams)

{
  "events": "mousedown, mousemove, mouseup"
}

Или более сложные конструкции:

{
  "events": "mousedown > mousemove > mouseup"
}

Здесь > означает последовательность во времени.


Поле update

update — это выражение Vega Expression Language, которое вычисляет новое значение сигнала при каждом событии.

Пример:

{
  "name": "mouseX",
  "value": 0,
  "on": [
    {
      "events": "mousemove",
      "update": "event.x"
    }
  ]
}

Контекст выполнения update

В выражениях доступны:

  • event — объект DOM события
  • datum — данные, если событие связано с визуальным элементом
  • доступ к другим сигналам через их имена

Пример: отслеживание координат мыши

{
  "signals": [
    {
      "name": "x",
      "value": 0,
      "on": [
        {
          "events": "mousemove",
          "update": "event.x"
        }
      ]
    },
    {
      "name": "y",
      "value": 0,
      "on": [
        {
          "events": "mousemove",
          "update": "event.y"
        }
      ]
    }
  ]
}

Система обновляет сигналы при каждом движении мыши, формируя реактивное состояние.


Условная обработка событий

Обработчики могут включать условия:

{
  "events": "mousedown",
  "update": "event.shiftKey ? x + 10 : x - 10"
}

Также возможно использование фильтрации прямо в events:

{
  "events": "mousedown[event.button === 0]"
}

Это позволяет разделять поведение без дополнительного кода.


Приоритет и множественные обработчики

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

{
  "on": [
    {
      "events": "mousedown",
      "update": "1"
    },
    {
      "events": "mousedown",
      "update": "2"
    }
  ]
}

Порядок важен: при совпадении условий обработчики выполняются последовательно.


Привязка к графическим объектам

Vega позволяет слушать события не только глобально, но и на уровне элементов сцены.

{
  "events": "symbol:click",
  "update": "datum.value"
}

Здесь:

  • symbol — тип графического элемента
  • click — тип события
  • datum.value — значение из связанного набора данных

Использование on для перетаскивания

Классический сценарий — drag-and-drop:

{
  "signals": [
    {
      "name": "dragX",
      "value": 0,
      "on": [
        {
          "events": "symbol:mousedown",
          "update": "x"
        },
        {
          "events": "window:mousemove",
          "update": "dragX + event.dx"
        },
        {
          "events": "window:mouseup",
          "update": "dragX"
        }
      ]
    }
  ]
}

Здесь используются:

  • window:mousemove — глобальные события
  • event.dx — изменение координаты
  • фиксация состояния после завершения взаимодействия

Поле on в Vega-Lite

В Vega-Lite напрямую поле on встречается в нескольких контекстах, главным образом:

1. Селекции (selections)

{
  "selection": {
    "brush": {
      "type": "interval",
      "on": "[mousedown[event.shiftKey], mouseup] > mousemove"
    }
  }
}

Здесь on управляет тем, как формируется интервал выбора:

  • начало выделения
  • изменение области
  • завершение действия

2. Условные интерактивные трансформации

Хотя Vega-Lite абстрагирует сигналы, on может использоваться для описания событий, которые управляют фильтрацией или выделением данных.


Комбинирование потоков событий

Одно из ключевых преимуществ on — возможность композиции:

Последовательности

"events": "mousedown > mousemove > mouseup"

Альтернативы

"events": "touchstart, mousedown"

Фильтры

"events": "mousemove[event.altKey && event.shiftKey]"

Взаимодействие с выражениями Vega

В выражениях update доступны математические и логические операции:

  • арифметика
  • тернарные выражения
  • доступ к сигналам
  • функции Vega Expression Language

Пример:

{
  "events": "mousemove",
  "update": "clamp(event.x, 0, width)"
}

Типичные ошибки при использовании on

1. Отсутствие фильтрации событий

Приводит к чрезмерному обновлению сигналов:

"events": "mousemove"

без ограничения по элементу или состоянию.


2. Конфликты между обработчиками

Несколько on могут перезаписывать значения сигналов, если логика не разделена.


3. Неверный контекст datum

Использование datum вне событий, связанных с mark-элементами, приводит к undefined.


Архитектурное значение on

Поле on является ключевым элементом реактивной модели Vega:

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

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