A modern browser is required for security, reliability, and performance. Contact us.

Feb 11 2013

Pie Charts, now with Gradient Filling

Lots of people love pie. Some people love pie charts. I personally love pie charts so much that I wanted mine to use a gradient fill. Now yours can too, and here’s how:

Drawing pie charts with canvas is relatively straightforward. Draw a complete arc of one color, and then an arc of another color to indicate the completed percent. Canvas even supports gradient fills, so we’re all set, right? Unfortunately, no. The linear and radial fills that canvas uses aren’t quite what we need. What we really need is a ‘polar gradient’. It’s a bit more work, but we can actually build one of these too. Using

createImageData
, get an array of pixels. Now, visualize this array as four quadrents with an origin in the center. Loop through every pixel and translate its cartesian coordinates to polar coordinates. Then, set the alpha channel of the pixel proportional to its theta value. Finally, insert this array of pixels into an offscreen canvas element with
putImageData
.

So what good does this gradient do us? Well, it means we can draw an arc of one color in the main canvas, and then use

drawImage
to overlay the gradient on top. By default, canvas uses the
source-over
composite mode. With this set, we’d get the proper arc with
drawImage
, but there would be some ‘extra’ gradient pixels from the source canvas. So, we set
globalCompositeOperation
to
source-atop
, which means that now
drawImage
will only copy pixels from the source canvas where there are pixels in the target canvas. Perfect. Now we just need the background. We take advantage of
globalCompositeOperation
a second time, and set it to
destination-over
. This way, it draws behind the gradient that’s already in the main canvas.

Here’s what the final pie chart looks like…

pieChart example

…and here’s the code to generate it…

pieChart({
fillPercent: 95,
backgroundStrokeColor: "#CCC",
foregroundStrokeColor: ["green", "blue"],
animationRate: 5000,
radius: 120,
stroke: 12,
container: document.getElementById('ff')
});

To learn more, check out the code here on GitHub.

—William Farrell

MojoTech

Share: