Поле on: события, запускающие выделение

В системе интерактивности Vega и Vega-Lite поле on определяет события, при которых изменяется состояние выделения (selection). Через него описываются пользовательские действия:

  • щелчки мышью;
  • двойные щелчки;
  • наведение курсора;
  • перетаскивание;
  • нажатия клавиш;
  • касания;
  • комбинированные события.

Поле on является центральным механизмом управления интерактивностью. Именно оно связывает визуальные элементы с DOM-событиями браузера и позволяет строить:

  • интерактивные фильтры;
  • подсветку данных;
  • динамические панели;
  • масштабирование;
  • выбор диапазонов;
  • реакцию на курсор;
  • кастомные пользовательские сценарии.

Базовая структура

В Vega-Lite поле on находится внутри описания выделения:

{
  params: [
    {
      name: "selectPoint",
      select: {
        type: "point",
        on: "click"
      }
    }
  ]
}

Здесь:

  • type: "point" — выделение отдельных точек;
  • on: "click" — событие, запускающее выделение.

Событийная модель Vega

Vega использует собственную декларативную систему событий поверх DOM API браузера.

Поддерживаются:

Категория Примеры
События мыши click, dblclick, mousemove
Кнопки мыши mousedown, mouseup
Наведение mouseover, mouseout
Колёсико wheel
Сенсорные события touchstart, touchmove
Клавиатура keydown, keyup
Комбинации [mousedown, mouseup] > mousemove

Простое событие

Выделение по клику

{
  params: [
    {
      name: "pointSelect",
      select: {
        type: "point",
        on: "click"
      }
    }
  ]
}

Выделение произойдёт при каждом клике по mark-элементу.


Выделение по наведению

Одним из самых распространённых сценариев является реакция на курсор.

{
  params: [
    {
      name: "hoverSelect",
      select: {
        type: "point",
        on: "mouseover"
      }
    }
  ]
}

Теперь объект активируется при наведении мыши.


Использование mousemove

Событие mousemove срабатывает постоянно при движении курсора.

{
  params: [
    {
      name: "tracking",
      select: {
        type: "point",
        on: "mousemove"
      }
    }
  ]
}

Такой подход используется:

  • в crosshair-индикаторах;
  • интерактивных линейках;
  • tooltip-системах;
  • графиках реального времени.

Двойной клик

dblclick

{
  params: [
    {
      name: "doubleClickSelection",
      select: {
        type: "point",
        on: "dblclick"
      }
    }
  ]
}

Типичные сценарии:

  • сброс состояния;
  • переход в режим редактирования;
  • закрепление выделения.

События нажатия кнопок мыши

mousedown

Срабатывает в момент нажатия.

{
  on: "mousedown"
}

mouseup

Срабатывает при отпускании кнопки.

{
  on: "mouseup"
}

Такие события критически важны для drag-and-drop взаимодействий.


События наведения

mouseover

Активируется при входе курсора в область элемента.

{
  on: "mouseover"
}

mouseout

Срабатывает при выходе курсора.

{
  on: "mouseout"
}

Пример очистки выделения:

{
  params: [
    {
      name: "hover",
      select: {
        type: "point",
        on: "mouseover",
        clear: "mouseout"
      }
    }
  ]
}

Поле clear

Хотя clear является отдельным свойством, оно тесно связано с on.

{
  params: [
    {
      name: "hoverState",
      select: {
        type: "point",
        on: "mouseover",
        clear: "mouseout"
      }
    }
  ]
}

Здесь:

  • mouseover создаёт выделение;
  • mouseout удаляет его.

Комбинированные события

Vega поддерживает сложные цепочки событий.

Drag-сценарий

{
  on: "[mousedown, mouseup] > mousemove"
}

Это означает:

  1. пользователь нажал кнопку;
  2. перемещает мышь;
  3. взаимодействие продолжается до mouseup.

Такая конструкция применяется для:

  • brush selection;
  • выделения области;
  • drag-панелей;
  • интерактивного масштабирования.

Brush selection и on

Пример интервального выделения:

{
  params: [
    {
      name: "brush",
      select: {
        type: "interval",
        on: "[mousedown, mouseup] > mousemove"
      }
    }
  ]
}

События с фильтрацией

Vega позволяет фильтровать события.

Только левая кнопка мыши

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

Правая кнопка мыши

{
  on: "mousedown[event.button === 2]"
}

Проверка клавиш-модификаторов

Событие с Shift

{
  on: "click[event.shiftKey]"
}

Событие с Ctrl

{
  on: "click[event.ctrlKey]"
}

Alt-клик

{
  on: "click[event.altKey]"
}

Множественные события

Можно указывать несколько событий.

{
  on: "click, mouseover"
}

Выделение будет происходить:

  • либо по клику;
  • либо при наведении.

Массив событий

Допускается массив.

{
  on: [
    "click",
    "mouseover"
  ]
}

Объектная форма события

В Vega доступна расширенная объектная конфигурация.

{
  on: {
    type: "click",
    filter: ["event.shiftKey"]
  }
}

Фильтрация через filter

Пример

{
  on: {
    type: "click",
    filter: [
      "event.shiftKey",
      "event.button === 0"
    ]
  }
}

Условия:

  • зажат Shift;
  • используется левая кнопка мыши.

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

События могут приходить из различных источников.

Источник view

{
  on: {
    type: "mousemove",
    source: "view"
  }
}

Источники событий

Источник Назначение
view Всё представление
window Глобальное окно браузера
scope Локальная область
event Текущий event-контекст

Использование window

Полезно для drag-взаимодействий.

{
  on: {
    type: "mousemove",
    source: "window"
  }
}

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


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

Нажатие клавиши

{
  on: "keydown"
}

Отпускание клавиши

{
  on: "keyup"
}

Проверка конкретной клавиши

Escape

{
  on: "keydown[event.key === 'Escape']"
}

Enter

{
  on: "keydown[event.key === 'Enter']"
}

Touch-события

Для мобильных устройств Vega поддерживает touch API.

Начало касания

{
  on: "touchstart"
}

Перемещение пальца

{
  on: "touchmove"
}

Завершение касания

{
  on: "touchend"
}

Wheel-события

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

{
  on: "wheel"
}

Ограничение wheel-событий

{
  on: "wheel[event.deltaY < 0]"
}

Такой фильтр реагирует только на прокрутку вверх.


Использование consume

Иногда необходимо предотвратить дальнейшее распространение события.

{
  on: {
    type: "wheel",
    consume: true
  }
}

Аналогично event.preventDefault().


Связь on и toggle

Поле toggle определяет поведение повторного выбора.

{
  params: [
    {
      name: "multi",
      select: {
        type: "point",
        on: "click",
        toggle: "event.shiftKey"
      }
    }
  ]
}

Здесь:

  • обычный клик заменяет выделение;
  • Shift-клик добавляет новые элементы.

Связь on и nearest

{
  params: [
    {
      name: "hover",
      select: {
        type: "point",
        nearest: true,
        on: "mousemove"
      }
    }
  ]
}

Теперь выбирается ближайшая точка к курсору.


Сложные drag-последовательности

Интервальное выделение

{
  params: [
    {
      name: "dragBrush",
      select: {
        type: "interval",
        on: "[mousedown, window:mouseup] > window:mousemove"
      }
    }
  ]
}

Особенности:

  • drag начинается внутри графика;
  • продолжается за его пределами;
  • завершается при отпускании кнопки.

События с debounce

В Vega поддерживаются механизмы оптимизации.

{
  on: {
    type: "mousemove",
    throttle: 50
  }
}

Событие будет срабатывать не чаще одного раза в 50 мс.


Использование debounce

{
  on: {
    type: "mousemove",
    debounce: 100
  }
}

Событие выполнится только после паузы.


Разница между throttle и debounce

Механизм Поведение
throttle Ограничивает частоту
debounce Ждёт завершения серии событий

Практический hover tooltip

{
  params: [
    {
      name: "hover",
      select: {
        type: "point",
        nearest: true,
        on: "mousemove",
        clear: "mouseout"
      }
    }
  ]
}

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


Практический multi-select

{
  params: [
    {
      name: "multiSelect",
      select: {
        type: "point",
        on: "click",
        toggle: "event.shiftKey"
      }
    }
  ]
}

Практический drag zoom

{
  params: [
    {
      name: "zoomArea",
      select: {
        type: "interval",
        on: "[mousedown, mouseup] > mousemove"
      }
    }
  ]
}

Использование событий в чистой Vega

В Vega события применяются не только в selections, но и в signals.

Пример signal

{
  "signals": [
    {
      "name": "cursorX",
      "on": [
        {
          "events": "mousemove",
          "update": "x()"
        }
      ]
    }
  ]
}

Поле events

В чистой Vega вместо on часто используется events.

{
  "events": "mousemove"
}

Это более низкоуровневый API.


События и signals

Signals позволяют:

  • отслеживать координаты;
  • хранить состояние интерфейса;
  • обновлять визуализацию;
  • управлять анимацией;
  • реализовывать сложную интерактивность.

Координаты курсора

{
  "signals": [
    {
      "name": "mousePosition",
      "on": [
        {
          "events": "mousemove",
          "update": "[x(), y()]"
        }
      ]
    }
  ]
}

События и производительность

Некоторые события генерируются чрезвычайно часто:

  • mousemove;
  • wheel;
  • touchmove.

Без ограничений они могут перегружать визуализацию.


Рекомендации по производительности

Использование throttle

{
  on: {
    type: "mousemove",
    throttle: 30
  }
}

Использование debounce

{
  on: {
    type: "mousemove",
    debounce: 100
  }
}

Минимизация тяжёлых обновлений

Не рекомендуется:

  • запускать сложные вычисления на каждый mousemove;
  • обновлять большие datasets без необходимости;
  • вызывать массовые re-render операции.

Типичные ошибки

Использование неподдерживаемых событий

{
  on: "hover"
}

События hover не существует.

Правильно:

{
  on: "mouseover"
}

Конфликт click и dblclick

Браузер сначала генерирует click, затем dblclick.

Это может вызывать двойные реакции интерфейса.


Проблемы drag-событий

Ошибка:

{
  on: "[mousedown, mouseup] > mousemove"
}

Если курсор покидает область графика, drag может оборваться.

Лучше:

{
  on: "[mousedown, window:mouseup] > window:mousemove"
}

Лучшие практики

Для hover

{
  on: "mousemove",
  clear: "mouseout"
}

Для drag

{
  on: "[mousedown, window:mouseup] > window:mousemove"
}

Для multi-select

{
  on: "click",
  toggle: "event.shiftKey"
}

Для производительности

{
  throttle: 50
}

Полная схема интерактивного выделения

{
  params: [
    {
      name: "interactiveSelection",
      select: {
        type: "point",
        on: "mousemove",
        nearest: true,
        clear: "mouseout",
        toggle: "event.shiftKey"
      }
    }
  ]
}

Такое выделение:

  • реагирует на движение курсора;
  • выбирает ближайшую точку;
  • очищается при уходе мыши;
  • поддерживает множественный выбор через Shift.