Signals - nyurik/vega GitHub Wiki

This wiki documents Vega version 2. For Vega 3 documentation, see vega.github.io/vega.

WikiDocumentationSignals

Signals are dynamic variables that drive interactive behaviors. They can be used throughout a Vega specification (e.g., with mark or data transform properties), and their values are determined by expressions or event streams. Event streams capture and sequence hardware events (e.g., mousedown or touchmove). When an event occurs, dependent signals are re-evaluated in their specification order. These new signal values propagate to the rest of the specification, and the visualization is re-rendered automatically. Signals corresponding to the top-level width, height, and padding properties are automatically available, and update in response to changes via the Runtime API.

A signal definition, and its use in the rest of a specification, looks something like this:

{
  "signals": [{
    "name": "indexDate",
    "streams": [{
      "type": "mousemove", 
      "expr": "eventX()", 
      "scale": {"name": "x", "invert": true}
    }]
  }],

  "data": [{
    "name": "index",
    "source": "stocks",
    "transform": [
      {
        "type": "filter",
        "test": "month(datum.date) == month(indexDate)"
      }
    ]
  }],

  "marks": [{
    "type": "rule",
    "properties": {
      "update": {
        "x": {"scale": "x", "signal": "indexDate"}
      }
    } 
  }]
}

Available Events

Property Description
click ...
dblclick ...
dragenter ...
dragleave ...
dragover ...
keydown ...
keypress ...
keyup ...
mousedown ...
mousemove ...
mouseout ...
mouseover ...
mouseup ...
mousewheel ...
touchend ...
touchmove ...
touchstart ...
wheel ...

Signal Properties

Property Type Description
name String A unique name for the signal. Reserved keywords (including datum, event, signals and function names) may not be used.
init Object | * The initial value of the signal. If init is an object with an expr property, the expression is evaluated to produce the signal's initial value. An additional scale property can also be specified to invoke a Scoped Scale Reference.
verbose Boolean By default, a signal only triggers an update when it evaluates to a new value (i.e., an update does not occur if a signal's new value is equal to its current value). If verbose is set to true, the signal will always trigger an update when its value is set.

Expression Values

Signal values can be determined in one of two ways: purely by other signals, or by event streams as well. If a signal is only dependent on other signals, two additional properties can be specified:

Property Type Description
expr Expression A string containing an expression (in JavaScript syntax) for the signal value. It is automatically reevaluated whenever used signal values change.
scale ScopedScaleRef A scale transform to apply to the expression. This can be particularly useful for inverting expression values (i.e., moving them from visual/pixel space to data space).

Event Stream Values

To have interaction events (e.g., mousedown, or touchmove) trigger changes in signal values, an additional streams property must be defined as an array of objects with the following properties:

Property Type Description
type EventSelector A string that uses the event selector syntax.
expr Expression A string containing an expression (in JavaScript syntax) that is reevaluated every time the specified events occur. event and datum variables are available for use here, corresponding to the captured DOM event and the data object backing the event's target item. For events that occur within the visualization, the following special event functions are also available:
eventX Number An x-coordinate relative to the visualization container element.
eventY Number A y-coordinate relative to the visualization container element.
eventXY Number Combining the above two functions into an object with x and y properties.
eventItem Item The event target item within the Vega scenegraph.
eventGroup Item The target's enclosing group mark item within the Vega scenegraph.

eventGroup takes an optional name argument to return a named ancestor group mark item. Only named ancestors of the event target are addressable here.

Similarly, the eventX, eventY, and eventXY take an optional argument that can either be the name or scenegraph item of an enclosing ancestor. If provided, coordinates are translated to the given group's coordinates.

For example, if a rectangle mark item in the following specification was clicked

{
  "marks": [{
    "name": "foo",
    "type": "group",

    "marks": [{
      "name": "bar",
      "type": "group",

      "marks": [{
        "type": "rect"
      }]
    }]
  }]
}

eventItem would return the specific rectangle that was clicked. eventGroup() would return the enclosing group mark item (bar), and eventGroup('foo') would return its parent. eventX and the others would return the position of the click relative to the entire visualization, whereas eventX('foo') would return the position of the click within the foo group mark.

Property Type Description
scale ScopedScaleRef A scale transform to apply to the expression. This can be particularly useful for inverting expression values (i.e., moving them from visual/pixel space to data space).

Event Stream Selectors

Event selectors specify the sequence of events ("event stream") that must occur in order to trigger an interactive behaviour. The syntax consists of the following:

Name Description
eventType Captures events of a specific type, for example mousedown, or touchmove. By default, this captures all events of the given type that occur anywhere on the visualization.
target:eventType Filters for only events that occur on the given target. The following targets are recognized:
markType Filters for events that occur on mark instances of the given type. All supported mark types are available. For example, rect:mousedown captures all mousedown events that occur on rect marks.
@markName Filters for events that occur on marks with the given name. For example, @cell:mousemove captures all mousemove events that occur within the mark named cell.
CSS selector The full gamut of CSS selectors can be used to capture events on elements that exist outside the visualization. D3 is used to capture these events, and the custom event functions, described above, are not available. For example #header:mouseover captures mouseover events that occur on the HTML element with ID header.
eventStream[filterExpr] Filters for events that match the given expression. The filter expression is specified using normal JavaScript syntax, and the event and datum variables are also available. Multiple expressions can also be specified. For example, mousedown[eventX() > 5][eventY() < 100] captures mousedown events which occur at least 5px horizontally, and no more than 100px vertically within the visualization.
streamA, streamB Merges individual event streams into a single stream with the constituent events interleaved correctly. For example, @cell:mousemove, mousedown[eventX() > 5][eventY() < 100] produces a single stream of @cell:mousemove and mousedown[eventX() > 5][eventY() < 100] events, interleaved as they occur.
[streamA, streamB] > streamC Captures streamC events that occur between streamA and streamB. For example, [mousedown, mouseup] > mousemove describes a stream of mousemove events that occur between a mousedown and a mouseup, otherwise known as a "drag" stream.

Scoped Scale Reference

When defining an interactive behaviour, it can often be useful to mix between the visual/pixel space of the rendered visualization, and its backing data space. For example, with brushing & linking a scatterplot matrix, the pixel extents of the brush are inverted to produce a data range to highlight points across all cells of the matrix. Scoped scale references help with this. They can be either an object, or the name of a top-level scale. In the latter case, the scale transform is applied as normal (transforming the signal value from the data domain to the visual range).

If the scope scale reference is an object, the following properties are available:

Property Type Description
name String The name of the scale.
scope String | SignalRef An expression or signal reference to a group mark that contains this scale. If no scope is specified, the scale is expected to be defined at the top-level.
invert Boolean If true, an inverse of the scale transform is applied (i.e., transforming the signal value from the visual range to the data domain).

For example, in the following specification, the group mark named cell is captured by the corresponding signal. This signal is used to lookup the x scale, which subsequently inverts the value of the start_x signal.

{
  "signals": [
    {
      "name": "cell",
      "streams": [
        {"type": "@cell:mousedown", "expr": "eventGroup('cell')"}
      ]
    },
    {
      "name": "start_x",
      "init": 0,
      "streams": [
        {
          "type": "mousedown", 
          "expr": "eventX()",
          "scale": {"name": "x", "scope": {"signal": "cell"}, "invert": true}
        }
      ]
    }
  ],

  "marks": [
    {
      "name": "cell",
      "type": "group",

      "scales": [{"name": "x", ...}],
      ...
    }
  ]
}

Using and Referencing Signals

Once defined, signals can be used throughout a specification. They are available for use within all expressions (e.g., with the filter or formula transforms), and a signal property is available with mark property value references. For other areas of the specification, a special signal reference ("SignalRef") can be used. A SignalRef is an object with a single signal property. For example,

{
  "signals": [{"name": "xMin", ...}, {"name": "xMax", ...}],

  "scales": [
    {
      "name": "x",
      "range": "width",
      "domainMin": {"signal": "xMin"},
      "domainMax": {"signal": "xMax"}
    }
  ]
}

Dot notation can also be used to access nested signal values. For example,

{
  "signals": [
    {
      "name": "brush_start",
      "init": {"x": 0, "y": 0},
      "streams": [{"type": "mousedown", "expr": "{x: eventX(), y: eventY()}"}]
    }
  ],

  "marks": [
    {
      "type": "rect",
      "properties": {
        "update": {
          "x": {"signal": "brush_start.x"},
          ...
        }
      }
    }
  ]
} 

The cursor signal

By default, Vega will style the mouse pointer when it is over a mark with a defined cursor property. However, for many interactive use cases, the cursor style should persist for the entire duration of the interaction. For example, consider the Budget Forecasts example. The cursor should be set to pointer when hovering over the slider control, and should remain a pointer when we start dragging it regardless of mouse position.

For these use-cases, Vega provides a specially named cursor signal. When the value of this signal is set, Vega uses it in lieu of any cursor properties set on marks. If the value is set to default, then Vega resumes using the mark-based cursor property. Below, we reproduce a snippet of the Budget Forecasts example that sets the cursor. As we see, a cursor property is specified for the symbol mark named handle. A cursor signal is also provided which determines its value based on whether a drag operation is currently occur. Thus, when the user's mouse is over the handle, it changes to a pointer; when the mouse moves off the handle, it reverts back to the default cursor; and, when the user begins to drag the slider handle, the cursor style remains fixed as a pointer regardless of its position.

"signals": [
  {
    "name": "dragging",
    "init": false,
    "streams": [
      {"type": "@handle:mousedown", "expr": "true"},
      {"type": "mouseup", "expr": "false"}
    ]
  },
  {
    "name": "cursor",
    "streams": [
      {"type": "dragging", "expr": "dragging ? 'pointer' : 'default'"}
    ]
  }
],

"marks": [
  {
    "name": "handle",
    "type": "symbol",
    "properties": {
      "hover": {
        "cursor": {"value": "pointer"}
      }
    }
  },
]