Signals - smclements/vega GitHub Wiki
Wiki ▸ Documentation ▸ Signals
Signals are dynamic variables that drive interactive behaviours. 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.
A signal definition, and it's 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"}
}
}
}]
}
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, or * | 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. |
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). |
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
Event | Type | Description |
---|---|---|
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. |
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). |
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.
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. |
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", ...}],
...
}
]
}
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"},
...
}
}
}
]
}