Определение signals: name, value, on

В Vega сигналы представляют собой базовый механизм реактивного состояния, который используется для управления динамическими значениями внутри визуализации. Каждый сигнал — это именованная переменная, которая может хранить значение, вычисляться на основе выражений и изменяться в ответ на события пользовательского взаимодействия или внутренние изменения системы рендеринга.

Сигналы задаются в массиве signals в спецификации Vega и имеют три ключевых свойства, определяющих их поведение: name, value и on.


Структура сигнала

Базовая форма определения сигнала выглядит следующим образом:

{
  "signals": [
    {
      "name": "example",
      "value": 0,
      "on": []
    }
  ]
}

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


Поле name

name определяет уникальный идентификатор сигнала в рамках всей спецификации Vega.

Основные свойства name:

  • должен быть уникальным в пределах массива signals
  • используется как переменная в выражениях Vega Expression Language
  • доступен в любом месте спецификации, где допускаются выражения
  • служит точкой привязки для событий и реактивных обновлений

Пример:

{
  "name": "mouseX",
  "value": 0
}

После объявления такого сигнала его можно использовать в выражениях:

{
  "encode": {
    "update": {
      "x": { "signal": "mouseX" }
    }
  }
}

Таким образом name формирует основу адресации состояния внутри графа визуализации.


Поле value

value задаёт начальное значение сигнала. Это значение используется при инициализации сцены до того, как начнут обрабатываться события и вычисляться реактивные зависимости.

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

  • задаёт начальное состояние сигнала
  • может быть любого допустимого типа: число, строка, массив, объект, boolean
  • может быть выражением Vega Expression Language
  • используется до первого срабатывания событий в on
  • обновляется динамически при активации триггеров

Пример с числом:

{
  "name": "threshold",
  "value": 10
}

Пример с выражением:

{
  "name": "centerX",
  "value": "width / 2"
}

В этом случае centerX вычисляется при инициализации сцены на основе текущих параметров layout.

Важный аспект реактивности

value не является статическим значением в классическом смысле. Оно может быть пересчитано при пересборке сцены или изменении внешних параметров, если используется выражение.


Поле on

on определяет набор правил, которые описывают, как сигнал изменяется в ответ на события. Это ключевой механизм реактивного программирования в Vega.

Каждый элемент массива on представляет собой правило обновления сигнала.

Общая структура:

{
  "name": "dragX",
  "value": 0,
  "on": [
    {
      "events": "mousedown",
      "update": "x()"
    }
  ]
}

Поле events

events определяет, на какие события подписывается сигнал.

События могут быть:

  • DOM-события (mousedown, mouseup, mousemove)
  • события сцены (window:resize)
  • события по конкретным элементам (@mark:mouseover)
  • составные селекторы событий

Пример:

{
  "events": "mousemove",
  "update": "x()"
}

Также поддерживаются сложные выражения:

{
  "events": "mousedown, touchstart"
}

или последовательности:

{
  "events": "mousedown[!event.shiftKey] > mousemove"
}

Поле update

update определяет выражение, вычисляющее новое значение сигнала при срабатывании события.

Принципы работы update:

  • выражение вычисляется в контексте события
  • доступ к текущим координатам (x(), y())
  • доступ к данным события через event
  • результат становится новым значением сигнала

Пример простого обновления:

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

В этом случае сигнал mouseX постоянно синхронизируется с координатой мыши по оси X.


Механика реактивного обновления

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

Цепочка обновления:

  1. происходит событие
  2. активируется правило on
  3. вычисляется update
  4. изменяется значение сигнала
  5. пересчитываются зависимые выражения
  6. обновляется сцена визуализации

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

Перетаскивание элемента

{
  "name": "dragging",
  "value": false,
  "on": [
    {
      "events": "mousedown",
      "update": "true"
    },
    {
      "events": "mouseup",
      "update": "false"
    }
  ]
}

Координаты drag-смещения

{
  "name": "offsetX",
  "value": 0,
  "on": [
    {
      "events": "mousedown",
      "update": "x() - datum.x"
    }
  ]
}

Сигналы как основа состояния визуализации

Сигналы выполняют роль глобального реактивного состояния внутри спецификации Vega. Они заменяют необходимость внешнего state-менеджмента, характерного для классических JavaScript-приложений.

Основные сценарии использования:

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

Взаимодействие name, value и on

Эти три поля образуют единую модель:

  • name задаёт идентичность сигнала
  • value определяет начальное состояние
  • on обеспечивает изменение состояния во времени

Пример комплексного сигнала:

{
  "name": "zoom",
  "value": 1,
  "on": [
    {
      "events": "wheel",
      "update": "clamp(zoom * pow(1.001, -event.deltaY), 0.5, 10)"
    }
  ]
}

Здесь:

  • name задаёт параметр масштабирования
  • value устанавливает стартовый zoom
  • on обеспечивает интерактивное изменение через колесо мыши