Plotting data points

In this tutorial we will use d3 line() function to draw the line chart below from a given set of points. line function will generate the data d attribute needed to draw a line using an svg path element.

Linechart


<!-- LineChart.svelte -->
<script>
  // D3 imports
  import { scaleTime, scaleLinear } from 'd3-scale';
  import { extent, max } from 'd3-array';
  import { line, curveBasis } from 'd3-shape';
  import { draw } from 'svelte/transition';
  import { onMount } from 'svelte';
  // Component imports
  import AxisLeft from './AxisLeftV5.svelte';
  import AxisBottom from './AxisBottomV5.svelte';
  import Labels from './Labels.svelte';
  import { csv } from 'd3-fetch';

  // Get data
  // let's use csv function from d3-fetch to download our data.
  // download data on: https://datavisualizationwithsvelte.com/data/natural_gas.csv
  onMount(() => {
    csv('/data/natural_gas.csv').then((csvData) => {
      data = csvData;
    });
  });
  // State variables
  let data = $state(null);
  let width = $state(0); // width will be set by the clientWidth
  const height = 350;
  const margin = { top: 10, right: 0, bottom: 20, left: 35 };

  // Computed values
  let xScale = $derived(
    data && width
      ? scaleTime()
          .domain(extent(data, (d) => new Date(d.date)))
          .range([margin.left, width - margin.right])
      : null
  );

  let yScale = $derived(
    data && width
      ? scaleLinear()
          .domain([0, max(data, (d) => +d.price + 4)])
          .range([height - margin.bottom, margin.top])
      : null
  );

  // Line function from D3 to create the d attribute for a path element
  // which will be our line.
  let lineGenerator = $derived(
    xScale && yScale
      ? line()
          .x((d) => xScale(new Date(d.date)))
          .y((d) => yScale(+d.price))
          .curve(curveBasis)
      : null
  );
</script>

<!-- bind width of the container div to the svg width-->
<div class="wrapper" bind:clientWidth={width}>
  {#if data && width && xScale && yScale && lineGenerator}
    <svg {width} {height}>
      <AxisBottom
        {width}
        {height}
        {margin}
        tick_number={width > 380 ? 10 : 4}
        {xScale}
        format={(d) => d.getFullYear()} />
      <AxisLeft {width} {height} {margin} {yScale} position="left" />
      <Labels
        labelfory={true}
        {width}
        {height}
        {margin}
        tick_number={10}
        yoffset={-50}
        xoffset={270}
        label={'$ price per million British thermal units ↑'} />

      <path
        in:draw={{ duration: 8000 }}
        d={lineGenerator(data)}
        stroke="#fcd34d"
        stroke-width={1.5}
        fill="none" />
    </svg>
  {/if}
</div>

What we do above additionally is to create the line generating function first:

let lineGenerator = $derived(
  xScale && yScale
    ? line()
        .x((d) => xScale(new Date(d.date)))
        .y((d) => yScale(+d.price))
        .curve(curveBasis)
    : null
);

and then in the markup we used the line() function like so to set the d attribute of our path element:

<path
  in:draw={{ duration: 2000 }}
  shape-rendering="crispEdges"
  d={lineGenerator(data)}
  stroke="#fcd34d"
  stroke-width={1.5}
  stroke-linecap="round"
  fill="none" />

We achieved the drawing animation using the draw from svelte and applying it to our path element.

Join our Discord to share your charts, ask questions, or collaborate with fellow developers!