Intro to Web-Based Visualization w/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 REPL, 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 Svelte!: an introduction to Svelte’s data bind and template tags.
  2. A Scaled Bar Chart using just <div> HTML tags.
  3. A Data-Driven Bar Chart that loads external data, and adds a color scale. Notice:
    1. Because the data is now stored externally, we need to make a call over a network. As we cannot anticipate how long this network request might take, it occurs asynchronously so as not to cause the browser to hang.
    2. One typical way for processing asynchronous data requests in Svelte is to use the onMount life cycle method, which is called when a component is first rendered onto the DOM.
    3. Try reverting back to the old definition of the scales by switching the $: to const; what happens? why?
      • Recall, when we initialize the component, the data is an empty array. So, the scales that get constructed have an empty domain.

        Instead, we want our scales to be reactive: i.e., automatically updating once the data has loaded. We do this by replacing the declaration (const or let) with the $: label (which is valid JavaScript!). Svelte automatically recomputes any statements labeled in this way, whenever they reference variables that update. ()

  4. Switching over to an SVG Bar Chart, but with y positions and height hardcoded.
    • The same bar chart but using a y-scale. Notice, we are using a band scale to discretize space.
  5. Compared to HTML, switching over to SVG allows us to more easily rotate between horizontal and vertical bars while preserving the metaphor of visual encoding (i.e., mapping data values to visual properties).
    • In-Class Activity: fill in definitions for the x/y scales and rect mark properties.
    • Solutions. Notice, the range of the y-scale is reversed (.range([height, 0]) instead of .range([0, height]) as before) and, similarly, the definitions for the rect marks’ y position and height are unusual. Why?
      • Recall that the location of the origin (0, 0) is in the top-left corner. So, why does that affect the scale and y-dimension properties? Try out other values of each, to make sure you better understand this unusual feature of SVG.
  6. To add axes, we need to establish a margin convention.
    1. Once we’ve done so, we can make calls to d3-axis methods to draw the axes for us.
    2. However, these methods generate SVG elements for us programmatically (i.e., in JavaScript). As a result, we can’t invoke them in the template directly (e.g., {d3.axisBottom}).
    3. Instead, we use a bind:this attribute to store a reference to a particular SVG element in a JavaScript variable, and then use that variable to draw axes.
      • bind:this is our mechanism for bridging from vanilla/traditional D3 code to Svelte+D3 code. Notice, the variables used in this attribute (i.e., xAxis and yAxis) can then participate in D3 calls like d3.select.
      • As a follow-up, consider how we might rotate the axes labels by chaining additional methods to our d3.axis calls.

Additional Milestones

We didn’t have time to go over these last few milestones, but we provide them for you to work through in your own time as they transform the bar chart step-by-step into a scatterplot.

  1. Switching from a bar chart to a dot plot. Notice:
    • We have switched from rect marks to circle marks—another element provided by SVG by default. Unlike rects, circles are positioned via cx/cy and sized by specifying a radius (r).
    • In contrast to rect marks, circle marks are positioned at specific points rather than filling in a spatial band. As a result, our x-scale has switched over to a point scale (scalePoint). Notice how we didn’t need to modify our axes code at all though—D3’s built in axes helpers did the right thing for us based on the scale types.
  2. Moving from a dot plot to a scatterplot. Notice, here, that our sizeScale actually uses a square root scale (d3.scaleSqrt). Why is that?
    • The sizeScale is used to scale the radius of the circle (r). If you remember, the radius of a circle is π * r2. If sizeScale was linear, doubling a data value would actually cause the circle to be four times as large.
  3. Finally, using d3-shape to redundantly encode region as both color and shape. SVG is capable of drawing arbitrary shapes via the path mark and d3-shape gives us a lot of helper methods to compute the path data (d attribute).

Reusing Example Code

As we mentioned in lecture, the visualization community has increasingly transitioned over to Svelte + D3.js over the last 2–3 years to reduce the amount of boilerplate code you need to write, and to simplify the structure of the code (e.g., template tags instead of the more complex D3 data join).

However, D3.js has been the de-facto library for web-based visualization since its release back in 2011. As a result, you’re likely to find a lot of modules and example code out there written in either vanilla D3, or following older design patterns for D3 (e.g., d3.selectAll(...).data().enter()).

This class’s approach of Svelte + D3.js is entirely backwards compatible with any tutorial/documentation code you find online and any examples you draw inspiration from (and we encourage you to make heavy use of examples, as they’re a major reason for D3’s success to this day). In particular, as we described above, bind:this={} will be your key mechanism for bridging Svelte HTML templates with vanilla D3 code. Here are some examples to further demonstrate this point:

  • d3-legend is a third-party package for automatically generating legends from scales, akin to d3-axis. Here’s how to use d3-legend with Svelte + D3 via bind:this, just like we do axes.

  • D3 has curated 59 different examples as reusable templates. Here are three examples of how to copy and paste the example code into a Svelte + D3 format.

    • Svelte + Beeswarm
    • Svelte + Sunburst
    • Svelte + Force-Directed Graph
    • Here’s an interesting exercise to consider: the above examples simply copy and paste the D3 chart template. How might you instead restructure any of these examples such that the chart template is available via a custom Svelte component and the options are passed in as attributes?
      • So, for instance, <Sunburst {data} width=1152 height=1152 /> instead of Sunburst(data, {width: 1152, height: 1152, ...})?