-
Notifications
You must be signed in to change notification settings - Fork 186
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
2,033 additions
and
1,120 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<script setup> | ||
|
||
import * as Plot from "@observablehq/plot"; | ||
import * as d3 from "d3"; | ||
import {shallowRef, onMounted} from "vue"; | ||
|
||
const olympians = shallowRef([ | ||
{weight: 31, height: 1.21, sex: "female"}, | ||
{weight: 170, height: 2.21, sex: "male"} | ||
]); | ||
|
||
onMounted(() => { | ||
d3.csv("../data/athletes.csv", d3.autoType).then((data) => (olympians.value = data)); | ||
}); | ||
|
||
</script> | ||
|
||
# Interactions | ||
|
||
Interaction can allow readers to read values out of a plot (details on demand), or to fluidly change a view of data without having to edit code (zoom and filter). There are a variety of ways to achieve interaction with Plot, including built-in interaction features and development techniques with frameworks such as Observable and React. | ||
|
||
## Pointing | ||
|
||
When looking at a scatterplot, the reader may wonder, *what abstract values does this dot represent?* | ||
|
||
The [pointer transform](../interactions/pointer.md) can provide an answer: it dynamically [filters](../transforms/filter.md) a mark such that only the data closest to the pointer (such as the mouse) is rendered. The pointer transform is often paired with the [tip mark](../marks/tip.md) for interactive tooltips, revealing exact values as the pointer moves over the plot. The tip can show additional fields not otherwise visible, such as the *name* and *sport* of Olympic athletes below. | ||
|
||
:::plot defer | ||
```js | ||
Plot.dot(olympians, { | ||
x: "weight", | ||
y: "height", | ||
stroke: "sex", | ||
channels: {name: "name", sport: "sport"}, | ||
tip: true | ||
}).plot() | ||
``` | ||
::: | ||
|
||
The [crosshair mark](../interactions/crosshair.md) uses the pointer transform internally to display a [rule](../marks/rule.md) and a [text](../marks/text.md) showing the **x** (horizontal↔︎ position) and **y** (vertical↕︎ position) value of the nearest data. | ||
|
||
:::plot defer | ||
```js | ||
Plot.plot({ | ||
marks: [ | ||
Plot.dot(olympians, {x: "weight", y: "height", stroke: "sex"}), | ||
Plot.crosshair(olympians, {x: "weight", y: "height"}) | ||
] | ||
}) | ||
``` | ||
::: | ||
|
||
These values are displayed atop the axes on the edge of the frame; unlike the tip mark, the crosshair mark will not obscure other marks in the plot. | ||
|
||
## Selecting | ||
|
||
Support for selecting points within a plot through direct manipulation is under development. If you are interested in this feature, please upvote [#5](https://github.com/observablehq/plot/issues/5). See [#721](https://github.com/observablehq/plot/pull/721) for some early work on brushing. | ||
|
||
## Zooming | ||
|
||
Support for interactive panning and zooming is planned for a future release. If you are interested in this feature, please upvote [#1590](https://github.com/observablehq/plot/issues/1590). | ||
|
||
## Animation | ||
|
||
Support for declarative animation is planned for a future release. If you are interested in this feature, please upvote [#166](https://github.com/observablehq/plot/issues/166). See [#995](https://github.com/observablehq/plot/pull/995) for some early work on a **time** channel. | ||
|
||
## Custom reactivity | ||
|
||
With the exception of render transforms (see the [pointer transform](https://github.com/observablehq/plot/blob/main/src/interactions/pointer.js) implementation), Plot does not currently provide incremental re-rendering (partial updates to previously-rendered plots) or animated transitions between views. | ||
|
||
That said, you can simply throw away an old plot and replace it with a new one! This allows plotting of dynamic data: data which can change in real-time as it streams in, or because it is derived in response to external inputs such as range sliders and search boxes. | ||
|
||
On Observable, you can use [viewof](https://observablehq.com/@observablehq/views) in conjunction with [Observable Inputs](https://observablehq.com/@observablehq/inputs) for interactivity. If your cell references another cell, it will automatically re-run whenever the upstream cell’s value changes. For example, try dragging the slider in this [hexbin example](https://observablehq.com/@observablehq/plot-hexbin-binwidth?intent=fork). In React, use [useEffect](https://react.dev/reference/react/useEffect) and [useRef](https://react.dev/reference/react/useRef) to re-render the plot when data changes. In Vue, use [ref](https://vuejs.org/api/reactivity-core.html#ref). For more, see our [getting started guide](../getting-started.md). | ||
|
||
You can also manipulate the SVG that Plot creates, if you are comfortable using lower-level APIs; see examples by [Mike Freeman](https://observablehq.com/@mkfreeman/plot-animation) and [Philippe Rivière](https://observablehq.com/@fil/plot-animate-a-bar-chart). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Crosshair mark | ||
|
||
The **crosshair mark** … | ||
|
||
## Crosshair options | ||
|
||
The following options are supported: | ||
|
||
… | ||
|
||
## crosshair(*data*, *options*) | ||
|
||
```js | ||
Plot.crosshair(flare, {path: "name", delimiter: "."}) | ||
``` | ||
|
||
Returns a new crosshair mark with the given *data* and *options*. | ||
|
||
## crosshairX(*data*, *options*) | ||
|
||
## crosshairY(*data*, *options*) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<script setup> | ||
|
||
import * as Plot from "@observablehq/plot"; | ||
import * as d3 from "d3"; | ||
|
||
</script> | ||
|
||
# Pointer transform | ||
|
||
When a chart gives an overview of a dataset, we sometimes want to focus on a certain region of interest, for instance to obtain details about specific data points—such as outliers in a scatterplot. | ||
|
||
**TODO** Most standard marks support the **tip** option, which enables a default interaction that displays, on demand, the channel values pertaining to the mark that the user points to. | ||
|
||
example 1: *scatterplot with tip* | ||
|
||
In the case of a simple encoding (say, position and color), these values are taken directly from the mark’s data. When a transform option (such as bin, group, etc.) is applied, the transformed channels (aggregate count, sum, top-5 modalities, etc.) are displayed instead. | ||
|
||
example 2: *bar chart with tip* | ||
|
||
More generally, interaction mechanisms expect a user gesture—such as pointing with the mouse or tapping on the screen, on mobile—and highlight the relevant point, or display details. In Plot, this is obtained by combining two distinct primitives. First, the interaction primitive listens to the browser events generated by user gestures and decides which data point (or points) is the target of the gesture—the selection. Second, a mark renders that selection. This composability is what allows, for example, the presentation of a fixed tip mark (without pointing), or conversely the highlighting of the *x* and *y* values of the data point selected by the user with a [crosshair](./crosshair.md) instead of a tip. | ||
|
||
Plot currently supports one built-in interaction primitive, the *pointer*, which selects the data point that is the closest to the location of the mouse—or, on mobile, the closest to the location of a tap gesture. Combining it with the *tip* mark creates the default “tooltip” that shows the data values of that point in a callout box. | ||
|
||
example 3: *pointer + dot* | ||
|
||
<!-- | ||
## Brush | ||
## Lasso | ||
## Keyboard | ||
## Zoom | ||
--> | ||
|
||
## Pointer options | ||
|
||
* **px** | ||
* **py** | ||
* **maxRadius** | ||
|
||
If **px** is not specified, falls back to **x1**, **x2**, or **x**. If **py** is not specified, falls back to **y1**, **y2**, or **y**. | ||
|
||
## pointer(*options*) | ||
|
||
```js | ||
Plot.pointer({x: "Date", y: "Close"}) | ||
``` | ||
|
||
Given a position along *x* and *y*, adds a pointer interaction to the given *options*, and returns the options. The interaction selects the closest point to the user gesture. It listens to the pointermove and pointerenter events on the plot’s svg element to detect the mouse or tap location. It then selects the data point *i* that is closest to that location, and rerenders the mark with the index [*i*]. If the distance in pixels from the pointer location to the selected data point is greater than **maxRadius**, the selection is empty. The interaction also listens to pointerdown events, and toggles the sticky behavior. When the behavior is sticky, pointermove events are ignored—allowing the user, for example, to select and copy the text in the tip mark. The pointerout events (when the mouse exists the region of the plot), also empty the selection, and rerender the mark. | ||
|
||
## pointerX(*options*) | ||
|
||
```js | ||
Plot.pointerX({x: "Date", y: "Close"}) | ||
``` | ||
|
||
Given a position along *x* and *y*, adds an horizontal pointer interaction to the given *options*, and returns the options. This is similar to **pointer**, except that the distance is computed (mainly) along the *x* dimension—with a tiny contribution of the vertical distance along the *y* axis to disambiguate ties. Intended to work with any mark that privileges the horizontal dimension for pointing, such as a vertical bar chart, a temporal line chart where time is horizontal, etc. | ||
|
||
## pointerY(*options*) | ||
|
||
```js | ||
Plot.pointerY({x: "frequency", y: "letter"}) | ||
``` | ||
|
||
Given a position along *x* and *y*, adds a vertical pointer interaction to the given *options*, and returns the options. This is similar to **pointer**, except that the distance is computed (mainly) along the *y* dimension—with a tiny contribution of the horizontal distance along the *x* axis to disambiguate ties. Intended to work with any mark that privileges the vertical dimension for pointing, such as a horizontal bar chart. |
Oops, something went wrong.