One of the best things about the HTML5 <canvas> element is that we can now do things like this:

Example of a pie chart

without needing a plug in. I recently had to implement a similar type of graphic in canvas for a project I’m working on, so I thought I’d turn it into a tutorial on how to make a pie chart.

Rather than just hardcoding the functions to make a specific pie chart, let’s write it so you can easily change the data. That way, you can reuse the code any time you need to display a pie chart. So, we’ll first create an array of data, an array of labels, and an array of colors:

var data = [120, 100, 140];
var labels = ["120", "100", "140"];
var colors = ["#FFDAB9", "#E6E6FA", "#E0FFFF"];

Data contains numbers that add up to 360 (so we can make a complete circle). Each number corresponds to a segment of the pie chart. Labels contains strings that will be used to label the segments of the pie chart, and colors contains colors that will be used to color the segments.

Next we can write a function, drawSegment that will draw one segment, specified with the parameter i, using these values:

function drawSegment(canvas, context, i) {
    context.save();
    var centerX = Math.floor(canvas.width / 2);
    var centerY = Math.floor(canvas.height / 2);
    radius = Math.floor(canvas.width / 2);

    var startingAngle = degreesToRadians(sumTo(data, i));
    var arcSize = degreesToRadians(data[i]);
    var endingAngle = startingAngle + arcSize;

    context.beginPath();
    context.moveTo(centerX, centerY);
    context.arc(centerX, centerY, radius, 
                startingAngle, endingAngle, false);
    context.closePath();

    context.fillStyle = colors[i];
    context.fill();

    context.restore();

    drawSegmentLabel(canvas, context, i);
}

In this code, we first save the context so we can restore it later (that way, it’s safe to use with other canvas code). Remember, when you save/restore the context, you’re basically putting the context back into the state it was in you saved it.

Next, we find the center of the canvas, to use as the center of the pie chart, and compute the radius of the circle.

The starting angle is the sum of all the data so far (since each data point corresponds to a segment of the circle), so I wrote a helper function to sum up the first i segments of the array (see below). For example, if we’re drawing the third segment, i will be 2, and we’ll add 120 and 100, for a starting angle of 220. One of the trickiest things about learning to draw circles and arcs in canvas is understanding the starting and ending angles and the direction you’re drawing the arc, so review Chapter 7 in Head First HTML5 Programming if you get confused about this (I know I do even though I’d done it lots of times now!).

The ending angle is just the starting angle plus the arcSize, which is just the number in the data array corresponding to the segment we’re drawing. Note that I’m converting everything from degrees to radians, using another helper function (see below), since that’s what the context method to draw the arc expects.

Now we’re ready to draw the arc. We move the canvas “pen” to the starting point, which is the center of the circle, draw the arc, and then close the path, so we get a pie wedge shape. Remember that the last argument to the context.arc method is the direction we’re drawing the arc; in this case, false means we’re drawing in a clockwise direction around the circle. The starting angle starts from the east side of the x axis, and goes around clockwise when we use positive angle measurements, so that’s why we draw the arcs in a clockwise direction.

Finally, we need to fill the arc, so we pick the color from the array, and fill the path we’ve just drawn. We restore the context, and we’re done drawing that segment. The last thing we do in the function is call another function, drawSegmentLabel, to draw the label.

Before we get to drawSegmentLabel, here are the helper functions, degreesToRadians and sumTo:

function degreesToRadians(degrees) {
    return (degrees * Math.PI)/180;
}
function sumTo(a, i) {
    var sum = 0;
    for (var j = 0; j < i; j++) {
      sum += a[j];
    }
    return sum;
}

The function drawSegmentLabel adds the labels to the pie chart. You might wonder: how are we going to get the text rotated to match the segments? The trick to rotating the text for the labels is to rotate the context – it’s like you’re picking up the canvas, moving it and rotating it, and drawing the text as as normal. By saving and restoring the context each time the function is called, we make sure that the context goes back to its normal position after each time we call the function.

function drawSegmentLabel(canvas, context, i) {
   context.save();
   var x = Math.floor(canvas.width / 2);
   var y = Math.floor(canvas.height / 2);
   var angle = degreesToRadians(sumTo(data, i));

   context.translate(x, y);
   context.rotate(angle);
   var dx = Math.floor(canvas.width * 0.5) - 10;
   var dy = Math.floor(canvas.height * 0.05);

   context.textAlign = "right";
   var fontSize = Math.floor(canvas.height / 25);
   context.font = fontSize + "pt Helvetica";

   context.fillText(labels[i], dx, dy);

   context.restore();
}

After saving the context, we figure out the center of the circle, and then the angle to rotate the context so we can draw the text. The angle is the same as the starting angle we used for the segment in the drawSegment function (ie, the sum of all the data so far, converted to radians).

Once we have the angle, we translate, or move, the top left corner of the context to the center of the circle and then rotate it (clockwise) to the angle that matches the starting angle of the segment. Then we adjust the horizontal starting point of the text so it’s at the edge of the pie chart (since the text is right aligned, we’re setting the right side of the text box here). I subtracted 10 pixels to get it a bit of space on the right side. We also adjust the vertical position of the text so it has a little space above it, and fits just below the top of each segment.

Once we’ve got the position for the text figured out, we set the text alignment and font size. Notice that I’m computing the size of the font based on the size of the canvas (the pie chart is also sized to the canvas). Feel free to play with the number 25 to adjust the font size until you are happy with it (and try changing the canvas size in the HTML to make sure it works at a variety of sizes).

Finally, we fill the text with the label for the segment.

Now all we need is the code to get the canvas and the context and call the drawSegment function to draw all the segments. This code assumes you’ve got a canvas with the id of “piechart”.

canvas = document.getElementById("piechart");
var context = canvas.getContext("2d");
for (var i = 0; i < data.length; i++) {
    drawSegment(canvas, context, i);
}

And here’s the HTML you need (adjust the canvas size and you should see the pie chart change size too!):

<canvas id="piechart" width="400" height="400">
</canvas>

Enjoy, and let me know if you make any pie charts. I’ve posted the full code on github; it’s a little different–it’s set up to create a PieChart object, and drawSegment and drawSegmentLabel are methods of the PieChart object rather than functions, but it is basically the same idea.

Don't miss out!!

Don't miss out on brain-friendly WickedlySmart updates early access to books and general cool stuff! Just give us your email and we'll send you something about once a week. 

You have Successfully Subscribed!