DEV Community

Cover image for Line Chart with Svelte and D3
AnupJoseph for Learners Just that

Posted on

Line Chart with Svelte and D3

This blog is second in a series of (unofficial) course notes for the Data Visualization with React and D3 series by Curran Kelleher. Read the introductory blog post here.

The next chart type in the course is a line-chart based on San Francisco temperature data, available here. We will use the scatter plot code from the last chart here. Some interesting things about the dataset , its temporal in nature so the chart type must be able to show that change properly.

The row function in the last post is note very useful here. Lets convert the temperature column in to numbers. Also one of the columns is a timestamp so let's use the JS Date constructor to handle that.

const  row  =  function  (data)  {
    data.temperature  =  +data.temperature;
    data.timestamp  =  new  Date(data.timestamp);
    return data;
};

onMount(async  ()  => {
    dataset  =  await  csv(
"https://gist.githubusercontent.com/curran/60b40877ef898f19aeb8/raw/9476be5bd15fb15a6d5c733dd79788fb679c9be9/week_temperature_sf.csv",
    row).then((data)  => {
        return  data;
    });
});
Enter fullscreen mode Exit fullscreen mode

I want to plot temperature on the Y-axis and timestamp on X-axis.Since X-axis is a timestamp we are going to use scaleTime from D3. I am going to modify the scales accordingly

$: xScale  =  scaleTime()
    .domain(extent(dataset, (d)  =>  d.timestamp))
    .range([0, innerWidth]);

$: yScale  =  scaleLinear()
    .domain(extent(dataset, (d)  =>  d.temperature))
    .range([innerHeight, 0]);
Enter fullscreen mode Exit fullscreen mode

Let's just pass the temperature and timestamp points to these scales in the scatter plot logic we had before. The Axis doesn't need to be changed. Let's change the circle logic as follows:

<circle
    cx={xScale(data.timestamp)}
    cy={yScale(data.temperature)}
    r="5"
    style="fill:firebrick"
/>
Enter fullscreen mode Exit fullscreen mode

Scatter plot of the timestamps

Now we just need to connect these points. And it just so happens that D3 has a function to do exactly that. The line function in D3 gives a path provided a set of points. We can also specify the curveNatural to the line function.

$: line_gen  =  line()
    .curve(curveNatural)
    .x((d)  =>  xScale(d.timestamp))
    .y((d)  =>  yScale(d.temperature))(dataset);
Enter fullscreen mode Exit fullscreen mode

Let's add this line as a path element to the svg <g> element. And style it as well so that it shows up as a line and not a closed path.

<path  d={line_gen} />
<style>
    path {
        fill: transparent;
        stroke: rgb(18, 153, 90);
    }
</style>
Enter fullscreen mode Exit fullscreen mode

Line chart
Yeah, I am not a fan of those red dots either. The line probably needs to be a bit thicker as well. Similarly we need to change the format of the ticks in the bottom axis. There are a few other stylistic changes as well and I am going to do them in a single sweep. This is the final chart produced:
Image description

And here's the code for the same:

If you want a simple line chart or use a line chart as base for something else, then this is what you probably want. I actually want to go a bit deeper and achieve a sort of hand drawing like effect.

The property we need is stroke-dasharray and stroke-dashoffset. Think of it like so, instead of drawing a continous path, we are drawing a dashed path. However the dashes themselves are pretty big, as big as the path required for this data or more infact. Okay, rather than reading through that terrible explanation you can read through Cassie Evans fantasic, interactive explanation here.

I am setting the stroke-dasharray to a nice large number.stroke-dashoffset is set to 0.

path {
    fill: transparent;
    stroke: rgb(18, 153, 90);
    stroke-width: 2.5;
    stroke-linejoin: round;
    stroke-dasharray: 4400;
    stroke-dashoffset: 0;
}
Enter fullscreen mode Exit fullscreen mode

Visually there's absolutely no difference. However the underlying changes allow us to create the animation. Let's do just that:

path {

    fill: transparent;
    stroke: rgb(18, 153, 90);
    stroke-width: 2.5;
    stroke-linejoin: round;
    stroke-dasharray: 4400;
    stroke-dashoffset: 0;
    animation: draw 8.5s  ease;

}
@keyframes  draw {
    from {
        stroke-dashoffset: 4400;
    }
    to {
        stroke-dashoffset: 0;
    }
}
Enter fullscreen mode Exit fullscreen mode

Initially the stroke-dashoffset is set at 4400. Hence the dash is outside the SVG as each dash is of 4400 as well. As the transition continues the dash slowly becomes visible in the SVG viewBox.
Line Chart

Now I actually wanted ever so slightly more than this. I wanted to have the circles we had earlier to appear on the path as the line moves ahead. I was unable to recreate the effect perfectly, but the effect is still pleasing so here goes. I am using the fly transition from Svelte. Let's modify circle accordingly.

<circle
    cx={xScale(data.timestamp)}
    cy={yScale(data.temperature)}
    r="3"
    in:fly={{ duration: 5000, delay: i  *  15 }}
/>
Enter fullscreen mode Exit fullscreen mode

And this is the chart produced by adding that code

Line chart with circles appearing GIF

Beautiful!! I really like the teasing effect of the dots, appearing seeming at once all together yet seperate.

Here's the final code for the chart -

That's all for today! Have a nice day!

Discussion (0)