Annotation & Interaction with Svelte + D3.js
Table of contents
Lecture Milestones
Here are snapshots of milestones we live-coded through lecture today. These links will load the Svelte Playground, and you are encouraged to tweak and play around with the example code to validate you understand the concepts.
A recording of how we worked through these milestones in lecture can be found here.
- Hello Horizontal Bar Chart!: our starting point for lecture, picking up roughly from where we left off on Monday.
- Notice: once we use the
d3.axisTophelper function (or any of its siblings) to generate the necessary axis elements and populate the<g>element, we can use Vanilla D3 methods to manipulate the generated axis (including customizing the ticks or, as shown here, inserting an axis title).
- Notice: once we use the
- Adding a Reference Line for the average life expectancy.
- We accomplish this by calculating the average using
d3.meanand then drawing the necessary visual elements (a<line>and<text>mark). - To simplify positioning, we put the
<line>and<text>marks inside a<g>element, which is translated along the x-axis to correspond to the calculated average life expectancy.- Note: during lecture, my reference line wasn’t accurately positioned (as Eric pointed out!). That was because I had translated the reference
<g>element only byxScaleforgetting that there was amargin.leftto account for as well. This bug is fixed in this code example!
- Note: during lecture, my reference line wasn’t accurately positioned (as Eric pointed out!). That was because I had translated the reference
- We accomplish this by calculating the average using
- Visual Popout Below the Reference
- We’re using a ternary operator here which is a convenient mechanism for specifying conditionals inline.
- You can read it as saying “if a data value is less than the average (
d.health < avg) then (?) use the color scale, else/otherwise (:) fill the bar in gray.”
- Switching over to a scatterplot to be able to explore a broader set of interactive annotations.
- This scatterplot depicts a fuller version of the same dataset as before: public health data for various countries sourced from the Gapminder foundation. But now, we’ve got data for the years spanning 1955–2005 whereas the bar charts worked with data for only one year.
- In addition to linear scales for the x/y axes, and an ordinal scale for fill color, we’ve also introduce a
sizeScaleto size the circles based on the population of each country.- Notice:
sizeScaleactually uses a square root scale (d3.scaleSqrt). This is because we’re scaling the radius of our circle elements (via therattribute). If you remember, the area of the circle is calculated asπ * r2. If we’d used a linear scale, doubling a data value would actually cause the circle to be four times as large.
- Notice:
- SVG elements can optionally contain a
<title>element (like the<circles>in our scatterplot) which yields a simple browser-based tooltip. - Note: this example also uses the third-party d3-svg-legend package which offers a convenient API that is equivalent to d3-axis but for drawing legends instead. An alternative to this package is to just manually create a legend using Svelte template tags (which is something Lab 6 will cover this week).
- Scrub through the years with a slider.
- The key idea here is with the
bind:value={year}attribute which binds together theinput[type="range"]slider widget with theyearvariable such that when the value of one changes the other updates—in either direction. That is, when we scrub on the slider,yearis automatically set to its value; and ifyearis changed as part of a JavaScript function, then the slider will correspondingly update.
- The key idea here is with the
- Animate through the years, once per second.
- Here, we use the
window.setIntervalfunction (which is a JavaScript function provided by all browsers) to automatically schedule a function to execute once per second (or,1000milliseconds).- A related function is
window.setTimeoutwhich you can think of as a timer: it executes the given function once, after a specified time delay.
- A related function is
- The function returns an ID which is useful to cancel future executions (e.g., when we hit the last year to prevent the animation looping).
- What would you need to change to keep the animation looping, while still having the play/pause buttons?
- Here, we use the
- Visual Popout Selected Continents using a dropdown menu (
<select>element).- The code changes here largely follow ideas we’ve explored in previous steps.
- Notice: as currently setup, the
<cirlces>are drawn in the order they appear in the backingdata. As a result, the visual popout effect is not as strong as it could be because, for every continent, there are highlighted circles that appear underneath gray circles. - A more polished alternative always draws the highlighted circles on top of the rest. We accomplish this by regenerating our backing
datato ensure that points corresponding to theselectedContinentappear at the end of the array. To make our lives easier, we use the spread syntax rather than trying to figure out some specific sorting algorithm/function.
- Drive the same visual highlighting but via the legend instead.
- In contrast to most of our examples this far, this example implements the interaction not via
bindbut via a method known as event handling. - We “listen” or “handle” specific kinds of input events (in this case,
clickanddblclick) to set the sameselectedContinentvariable as the previous step. - As a result, without a lot more additional work, we’re able to hook into the interaction technique we built in the previous step: you can choose a continent either from the dropdown or by clicking a legend item.
- And a few additional lines of code allow us style the legend’s swatches and labels to make obvious which continent is currently highlighted.
- In contrast to most of our examples this far, this example implements the interaction not via
- Select a Country to view its trajectory across years
- Notice: in this example, we’re following an event handling approach but in the Svelte template (
on:clickandon:dblclick) rather than in JavaScript/Vanilla D3 as the previous example. Despite the difference in syntax, the logic works in generally the same way.
- Notice: in this example, we’re following an event handling approach but in the Svelte template (
- A connected scatterplot for the selected country.
- Connecting the individual points together is trickier than it initially appears. SVG
<lines>(like the one we used in step 2) can only ever connect a pair of points (a start and end). - Thus, what we need are a series of line segments and, although we could manually construct them using a Svelte
{#each}, this is such a common operation that D3 provides ad3.linegenerator function. - Importantly, the output of
d3.lineis passed to an SVG<path>element. This results in one smooth line, rather than a series of individual line segments; allowing us more flexibility in how the line should curve. - For example, we might use curveNatural to connect the selected country’s points.
- Connecting the individual points together is trickier than it initially appears. SVG
A Tour through the Interaction Zoo
In future lectures, we will survey a broader set of interaction techniques. However, we’re releasing these examples now to help inspire your final project ideation. We’ve grouped the techniques by the different categories taken from a 2007 research paper by Ji Soo Yi et al..
- Select: identify something as interesting
- A simple click to select
- Select on hover.
- Selecting points on a map
- Accelerated selection via a Voronoi diagram. D3 offers a module called d3-delaunay to compute these diagrams.
- Generalized Selection via Interactive Query Relaxation and the accompanying research paper by Jeff Heer et al.
- Connect: show me related items
- Brushing & Linking a scatterplot matrix. The equivalent visualization constructed with D3
- How the Tax Burden Has Changed
- Abstract/Elaborate: show me more or less detail
- Simple Tooltips
- Direct dynamic labeling
- DimpVis, hovering shows a timeline (in the style of a connected scatterplot) and points can be dragged along the timeline to navigate years
- How the Recession Reshaped the Economy, in 255 Charts
- Overview + Detail and the equivalent chart constructed with D3.
- Interactive Re-Binning on Detail.
- Gooey Exploding Scatterplot
- Filter: show me something conditionally
- Query Widgets and an example constructed with Svelte
- Job Voyager with regular expression text queries
- Cross Filtering
- Cross Filtering + Re-Aggregation
- Zipdecode with highlighting and zooming based on each individual digit of a zip code.
- Scented Widgets by Wesley Willett, Jeffrey Heer, Maneesh Agrawala
- Explore: show me something else
- Geometric Zooming scales the pixel space. You’re moving closer to the marks, or the marks are moving closer to you (i.e., they appear larger).
- Semantic Zooming scales data space (i.e., the
domainof a scale function but preserves therange; thus, points spread out when zoomed in). - Zoomable Choropleth for an alternate semantics when zooming (i.e., when you zoom into a state, the state breaks up into constituent counties).
- Reconfigure: show me a different arrangement
- Index Chart interactively re-normalizes the data based on the index point.
- Reorderable Matrix where rows/columns can be dragged and dropped to reorder them a la Bertin’s looms.
Additional Resources
- Gregor Aisch’s keynote at the Information+ 2016 Conference on interactive visualizations in news media.
- The death of interactive infographics?, a blog post reflecting on Aisch’s keynote by Dominikus Baur
- In Defense of Interactive Graphics, a response by Gregor Aisch