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.

  1. Hello Horizontal Bar Chart!: our starting point for lecture, picking up roughly from where we left off on Monday.
    • Notice: once we use thed3.axisTop helper 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).
  2. Adding a Reference Line for the average life expectancy.
    • We accomplish this by calculating the average using d3.mean and 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 by xScale forgetting that there was a margin.left to account for as well. This bug is fixed in this code example!
  3. 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.”
  4. 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 sizeScale to size the circles based on the population of each country.
      • Notice: sizeScale actually uses a square root scale (d3.scaleSqrt). This is because we’re scaling the radius of our circle elements (via the r attribute). 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.
    • 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).
  5. Scrub through the years with a slider.
    • The key idea here is with the bind:value={year} attribute which binds together the input[type="range"] slider widget with the year variable such that when the value of one changes the other updates—in either direction. That is, when we scrub on the slider, year is automatically set to its value; and if year is changed as part of a JavaScript function, then the slider will correspondingly update.
  6. Animate through the years, once per second.
    • Here, we use the window.setInterval function (which is a JavaScript function provided by all browsers) to automatically schedule a function to execute once per second (or, 1000 milliseconds).
      • A related function is window.setTimeout which you can think of as a timer: it executes the given function once, after a specified time delay.
    • 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?
  7. 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 backing data. 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 data to ensure that points corresponding to the selectedContinent appear 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.
  8. 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 bind but via a method known as event handling.
    • We “listen” or “handle” specific kinds of input events (in this case, click and dblclick) to set the same selectedContinent variable 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.
  9. 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:click and on:dblclick) rather than in JavaScript/Vanilla D3 as the previous example. Despite the difference in syntax, the logic works in generally the same way.
  10. 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 a d3.line generator function.
    • Importantly, the output of d3.line is 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.

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..

Additional Resources