Canvas pie

February 5th, 2008. Tagged: canvas, JavaScript

UPDATE: Translation in Brazilian Portuguese here, thanks Maujor!

OK, so you have an HTML table. Let's turn it into a pie chart with a bit of javascript.

We'll be using the canvas tag, so the browser has to support it. For IE - well, you still have the table. That's why we'll call it progressive enhancement. Unobtrusive too. Here's a screenshot:

» The demo is here (refresh for new colors)

Here are the ingredients to the recipe:

  1. One <canvas> tag
  2. One <table> full of data
  3. javascript to get the data from the table
  4. javascript to plot the data on the canvas

One canvas tag

<canvas id="canvas" width="300" height="300"></canvas>

One table full of data

This is a bare bone unstyled old school table.

<table id="mydata">
    <tr>       <th>Lang</th><th>Value</th> </tr>
    <tr><td>JavaScript</td>  <td>100</td>  </tr>
    <tr><td>       CSS</td>  <td>200</td>  </tr>
    <tr><td>      HTML</td>  <td>300</td>  </tr>
    <tr><td>       PHP</td>  <td> 50</td>  </tr>
    <tr><td>     MySQL</td>  <td> 30</td>  </tr>
    <tr><td>    Apache</td>  <td> 10</td>  </tr>
    <tr><td>     Linux</td>  <td> 30</td>  </tr>

javascript to get the data from the table

First, some setup. Let's tell the script which is the ID of the data table, the ID of the canvas tag and which column contains the data:

// source data table and canvas tag
var data_table = document.getElementById('mydata');
var canvas = document.getElementById('canvas');
var td_index = 1; // which TD contains the data

Next, select all table rows, then loop through the rows, selecting all TDs. Add the data we need to a data array. While at it, run a total of the data in the column and also create an array of random colors. Paint each row with the selected color. (we'll see the actual getColor() in a bit.)

var tds, data = [], color, colors = [], value = 0, total = 0;
var trs = data_table.getElementsByTagName('tr'); // all TRs
for (var i = 0; i < trs.length; i++) {
    tds = trs[i].getElementsByTagName('td'); // all TDs
    if (tds.length === 0) continue; //  no TDs here, move on
    // get the value, update total
    value  = parseFloat(tds[td_index].innerHTML);
    data[data.length] = value;
    total += value;
    // random color
    color = getColor();
    colors[colors.length] = color; // save for later
    trs[i].style.backgroundColor = color; // color this TR

javascript to plot the data on the canvas

Time for the fun part, the drawing! First, we need to create a context object. Then figure out the raduis of the pie and the center, all based on the width/height pf the canvas tag:

// get canvas context, determine radius and center
var ctx = canvas.getContext('2d');
var canvas_size = [canvas.width, canvas.height];
var radius = Math.min(canvas_size[0], canvas_size[1]) / 2;
var center = [canvas_size[0]/2, canvas_size[1]/2];

Next, let's loop through data and paint pieces of the pie. To draw a piece of pie, you basically need to call these methods of the context object:

  • beginPath() - to start the piece of the pie
  • moveTo() - to set the pencil in the center
  • arc() - draw a piece of a circle
  • lineTo() - finish the circle piece with a line back to the center
  • closePath() and fill() but set the fill color first.

Here's the actual code for this part, hopefully the comments help:

var sofar = 0; // keep track of progress
// loop the data[]
for (var piece in data) {
    var thisvalue = data[piece] / total;
    ctx.moveTo(center[0], center[1]); // center of the pie
    ctx.arc(  // draw next arc
        Math.PI * (- 0.5 + 2 * sofar), // -0.5 sets set the start to be top
        Math.PI * (- 0.5 + 2 * (sofar + thisvalue)),
    ctx.lineTo(center[0], center[1]); // line back to the center
    ctx.fillStyle = colors[piece];    // color
    sofar += thisvalue; // increment progress tracker


Here's the function that gives a random color:

    // utility - generates random color
    function getColor() {
        var rgb = [];
        for (var i = 0; i < 3; i++) {
            rgb[i] = Math.round(100 * Math.random() + 155) ; // [155-255] = lighter colors
        return 'rgb(' + rgb.join(',') + ')';

C'est tout! Enjoy your pie :D

UPDATE: Comment by Zoltan below, if you use Explorer Canvas, you can make this work in IE with only this:
<!--[if IE]><script type="text/javascript"

Tell your friends about this post: Facebook, Twitter, Google+

Sorry, comments disabled and hidden due to excessive spam. Working on restoring the existing comments...

Meanwhile, hit me up on twitter @stoyanstefanov