Using paths in canvas


The previous lesson taught how to render basic shapes on a canvas element. But as for SVG, some chart types requires some complex shapes that we used to render using the path SVG element.

Fortunately, canvas comes with a handy Path2D function that solves this exact problem. Let's see how it works.

Members only
6 minutes read

Remember the area chart?

Before we start using canvas, let's remind quickly how to build an area chart with react and d3.

Everything starts with a dataset.

const data = [
  {x:1, y: 90},
  {x: 2, y: 12},
  {x: 3, y: 34},
  {x: 4, y: 53},
  {x: 5, y: 98},
]

Then, d3.area() is used to create an area generator. This generator (called areaBuilder below) transforms a blob of data into a string that can be used as the d argument of a path SVG shape.

// create a shape generator.
// areaBuilder will be a function that takes data as input and returns a SVG path
const areaBuilder = d3
  .area()
  .x(d => xScale(d.x))
  .y1(d => yScale(d.y))
  .y0(yScale(0));

// call the function with the dataset
const areaPath = areaBuilder(data);
// M0,0 L57.778,277.333 L115.556,199.111 ... Z

The output areaPath can now be passed to a path SVG element!

<path d={areaPath} fill="#9a6fb0" />

It results in a minimal area chart:

import * as d3 from 'd3';

const MARGIN = { top: 20, right: 20, bottom: 20, left: 20 };

export const AreaChart = ({ data, width, height }) => {
  // bounds = the drawing area, once the margins are removed
  const boundsWidth = width - MARGIN.right - MARGIN.left;
  const boundsHeight = height - MARGIN.top - MARGIN.bottom;

  // scales, computed from the data and the bounds
  const xScale = d3
    .scaleLinear()
    .domain(d3.extent(data, (d) => d.x))
    .range([0, boundsWidth]);

  const yScale = d3
    .scaleLinear()
    .domain([0, d3.max(data, (d) => d.y)])
    .range([boundsHeight, 0]);

  // d3.area() returns a function that turns the data into an SVG path
  const areaBuilder = d3
    .area()
    .x((d) => xScale(d.x))
    .y1((d) => yScale(d.y))
    .y0(yScale(0));

  const areaPath = areaBuilder(data);

  return (
    <svg width={width} height={height}>
      <g transform={`translate(${MARGIN.left},${MARGIN.top})`}>
        <path d={areaPath} fill="#9a6fb0" fillOpacity={0.4} />
      </g>
    </svg>
  );
};

A very basic area chart made using React and the area() function of d3.js.

❌ The Problem

D3 does an amazing job here!

It transforms our dataset into a string called areaPath that looks like M0,0 L57,277 L115,199 ... Z, which we can directly pass to an SVG path element.

But how can we use this path data to draw on a canvas instead of SVG?

✅ Solution: Path2D

Luckily, there's a lifesaving feature in the Canvas API: Path2D. Its usage looks like this:

Oh no! 😱

It seems like you haven't enrolled in the course yet!

This is a lesson from the D3 ❤️ React course, where you learn to create bespoke, interactive graphs with d3.js and React.

Enrollment is currently closed. Join the waitlist to be notified when doors reopen:

Or Login