Graphs

section icon home home icon

D3 in Svelte, the D3 way - Pattern 1

link icon

Minimal example of how to use class-based D3 - rather than Svelte - for DOM manipulation, making sure graph re-drawing/responsiveness only triggers from Svelte when necessary by using reactive statements and declarations.

Main points

  • Width and height of the graph are controlled by the width and height of the graph container, which are defined as bound variables in Svelte. They are passed in as graph props, which are defined as reactive declarations.
  • The graph itself is defined entirely in D3, using a class-based graph template (though it doesn't have to be class-based), together with data and props.
    To listen for updates to the data or props (in this example props only, but it would work the same way with the data), we define the graph-drawing logic inside of a reactive statement. Svelte will only run the logic inside of the reactive statement when one or more of the variables that the statement depends on is updated. This will prevent any unnecessary triggers of the graph-drawing logic.
  • This approach doesn't use onMount at all to draw the graph when the graph component's wrapper DOM element is loaded on mount of the component. Instead, we perform a check inside the reactive declaration for the wrapper DOM element. This does essentially the same.
    Alternatively, we can call onMount with just the bit of the logic that inserts the svg element into the container DOM element, and then use a reactive declaration for the rest of the logic, including passing in reactive props and drawing the graph.
    The latter approach is more Svelt-y and more 'correct', as it uses onMount as it is intended, namely to perform some action that depends on the component having just mounted. However, I have found the two ways to work identically, and the former to 'look' a little bit cleaner.

Code highlights


resize window to see responsiveness

D3 in Svelte, the D3 way - Pattern 2

link icon

Minimal example of how to use class-based D3 - rather than Svelte - for DOM manipulation. Similar to above, but instead of using reactive statements and declarations from Svelte, we use onMount, together with a resize event.

Main points

  • Instead of binding the clientWidth and clientHeight of the container div of the graph to variables which are then passed as props to the graph, we use don't pass any width and height related props, but instead get them from inside the graph-drawing logic with
    const width = container .getBoundingClientRect() .width
  • Thus, instead of having reactive props, the props passed to the graph only contain constants.
  • The graph is initialised from onMount, and because the responsive width and height are no longer passed in as props, we need a separate resize event listener to be able to update the graph based on the dimensions of the container, if we want it to be responsive.

Disadvantages to reactive declaration approach

  • If anything needed to update in the graph because of updating data or props, we would need to put the graph-drawing logic in an afterUpdate as well. This might be ok if the changing variables only change very rarely, but not ok when they update continously, as is the case for example with a scroller. The reason for that is that the afterUpdate logic might fire more often that intended.
  • We need to write an extra event listener for resize and some not very Svelt-y code inside the graph logic, e.g. getting width of the container with
    getBoundingClientRect(), when we could instead just bind the container's dimensions to variables which are then passed as props to the graph, making it both cleaner and eliminating the need to use a resize event.

Code highlights


resize window to see responsiveness

D3 in Svelte, the Svelte way - Pattern 1

link icon

Minimal example of how to use Svelte for svg graphs, using D3 only for helper functions (e.g. scales) and for axes. Graph is responsive based on the width of the container and uses only reactive declarations and statements.

Main points

  • Width and height of the graph are based on the width and height of the container, which we obtain with Svelte binding, e.g. bind:clientWidth=w. These are also used to base other values on, such as fill, and make them responsive.
  • Scales and axes are defined as reactive declarations as they depend on the width and height (w and h variables).
  • Axes are called on a bound group element inside a reactive statement.
  • All the graph-drawing logic happens declaratively in the DOM, and the data is looped-over with a Svelte each statement.

Code highlights


resize window to see responsiveness

Svlete with D3 and Svelte Transitions

link icon

Svg-based graph done entriely in Svelte (other than scales), including different transitions for elements that enter and leave the DOM.

Main points

  • The use case here is when we have dynamic data such that we want DOM elements corresponding to each datum to have (possibly different) transitions as they enter, update in, or leave the DOM. In other words, we want to imitate the transitions possible inside a D3 general update pattern with enter, update and exit, entirely from Svelte. Note that when adding the data-bound DOM elements in a Svelte each block, you have to make sure that you can provide a key with unique ids, i.e. you need to use a keyed each block.
  • We use a fly transition imported from svelte/transition with some optional parameters.
  • For the circles entering the DOM, we use in:fly with options to make each circle enter the DOM from a -300px horizontal position, and with each circle entering 200 ms after the previous.
  • For the circles leaving the DOM, we use out:fly with options to make the circles fly off 200px to the right before disappearing.
  • Finally, for any updating DOM elements, e.g. circles that stay on but change colour or size, we simply add a CSS transition to the circles with transition: all 1s;

click on buttons to see graph