Feb 11 2013

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…

…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