Play

Creating Pie Charts with CSS3

After playing around and creating flags with CSS3, it dawned on me that the same methods used there could be used to create Pie charts. Keep in mind that this technique is currently cutting-edge. It only works in the latest versions of Firefox, Chrome,and Safari UPDATE and Opera; and requires browser-specific extensions to CSS to pull off, but it does use elements in the current CSS3 specification, so it stands that eventually the rest of the web will catch up.

UPDATE 2 I've completely overhauled this technique. Here's an explination of how and the new code/example on CodePen

First off, you need to draw a circle, which is easy enough, simply create a square <div> and set border-radius to be half it's height/width

.circle {
position:absolute;
background-color:red;
width:200px;
height:200px;
-moz-border-radius:100px;
-webkit-border-radius:100px;
border-radius:100px;
}

Next, we have to cut that in half. Normally, one would smply draw a half circle like so:

.half-circle {
position:absolute;
background-color:red;
width:100px;
height:200px;
-moz-border-radius:100px 0 0 100px;
-webkit-border-radius:100px 0 0 100px;
border-radius:100px 0 0 100px;
}

However, this method becomes problematic, since we'll have to rotate it. The half-circle above would rotate at it's centre (left-50,top-100). Combine that with the difference  in width and length,  it would require a lot of horizontal shifting to ensure the centres line up properly. That's a bunch of math or annoying trial and error. Thankfully, there's an easier way. Draw a full circle and use the old-school clip property to only show half:

.pie {
position:absolute;
width:200px;
height:200px;
clip:rect(0px,100px,200px,0px);
-moz-border-radius:100px;
-webkit-border-radius:100px;
border-radius:100px;
}

NOTE: you could use the original half-circle method and set the CSS3 transform-origin property to right, center to move the rotation point. But experimentation with this method revealed that using transform-origin in Safari caused that browser to ignore the border-radius setting (in Safari ver. 4.04 on Windows at least). So for now, stick with full circle and clip.

Now we have a half circle that will rotate around the centre of it's flat edge.

Next, we need to hide it in a way that allows us to show only the part we need to see. So we drop it into another square <div> and clip it too, to display the opposite half.

.hold {
position:absolute;
width:200px;
height:200px;
clip:rect(0px,200px,200px,100px);
}

We're clipping the hold <div>, because we need it to rotate around the same point as the circle.

now we stack all the pie elements on top of each other, and rotate the pie pieces by their appropriate percentage amount — (360*(x*100)) where x is the percentage value — and also rotate each hold div the cumulative amount of each previous pie piece's rotation. So given the following html:

<div id="piece1" class="hold">
<div class="pie"></div>
</div>
<div id="piece3" class="hold">
<div class="pie"></div>
</div>
<div id="piece3" class="hold">
<div class="pie"></div>
</div>

we can add the following CSS to create a pie chart with three equal pieces:

#piece1 {

}
#piece1 .pie {
color:red;
-moz-transform:rotate(120deg);
-webkit-transform:rotate(120deg);
-o-transform:rotate(120deg);
transform:rotate(120deg);
}
#piece2 {
-moz-transform:rotate(120deg);
-webkit-transform:rotate(120deg);
-o-transform:rotate(120deg);
transform:rotate(120deg);
}
#piece2 .pie {
color:green;
-moz-transform:rotate(120deg);
-webkit-transform:rotate(120deg);
-o-transform:rotate(120deg);
transform:rotate(120deg);
}
#piece3 {
-moz-transform:rotate(240deg);
-webkit-transform:rotate(240deg);
-o-transform:rotate(240deg);
transform:rotate(120deg);
}
#piece3 .pie {
color:blue;
-moz-transform:rotate(120deg);
-webkit-transform:rotate(120deg);
-o-transform:rotate(120deg);
transform:rotate(120deg);
}

Now, the more astute readers will notice that this works great for pie pieces that are 50% of the pie or less. How can you draw larger pieces? There are two methods. The easiest is to simply draw a full circle and then overlay the other pieces on top. But if you want a discrete piece that you can potentially move out for emphasis, you'll need to do two things: remove the clip on the hold <div>, and add a second pie piece as filler.

.hold.gt50 {
clip:rect(auto, auto, auto, auto);
}
.pie.fill {
-moz-transform:rotate(180deg) !important;
-webkit-transform:rotate(180deg) !important;
transform:rotate(180deg) !important;
}

That's it.

Here's the code in action

Tags: , ,
2010.02.20 12:30 PM | Permalink 9 Comments

Comments

  1. Oh also, I changed all the pixel measurements to em's so you can change the size of the pie graph easily by changing the text-size in the circle class :)
    Kus on
  2. Would just like to say thanks for your tutorial! It has helped me making a circle/pie graph jQuery timer. You can see it here: http://blakek.us/labs/jquery/css3-pie-graph-timer/
    Kus on
  3. Hey no, The numbers are added by after the fact by a jQuery plugin I use. You can turn off JavaScript, reload the page and then the numbers disappear. Or use your browser's "view source" feature to see the unformatted code. Also You could also use the plugin's own "Vierw Source" button that appears when you hover over the code snippets. It pops up a new window without the formatting and numbers. Or you could just bitch and moan some more. Totally your choice ;)
    Patrick Denny on
  4. is there any way you could embed your code more obnoxiously? there's nothing worse than trying to play with your code than having to go back and remove all those fucking numbers. it's on line twenty-three, yeah? is it REALLY on line twenty-three? just making sure. otherwise, spot on technique.
    no on
  5. I'm sorry Thomas, but I have a team of thugs currently descending on your location, Web IDE in hand, to *FORCE* you to use these pie charts. resistance is futile.
    Patrick Denny on
  6. Sorry, but it looks like something straight out of the 90s. From a visual and information design perspective, I would avoid those pie charts at all costs.
    Thomas on
  7. Brilliant! A more practical use of CSS3 than my CSS3 flags, I will admit!
    Codesquid on
  8. for a low internet speed user, it will be more useful rather than using images.. thx for the article :D
    Taufik on
  9. Well that's kind of fun. Browser support for canvas and SVG is getting so good that this isn't so practical -- but it is fun! I do want to officially register one complaint though: the list of browsers and their percentages... that is tabular data. You should have used a table tag not a bunch of div and span tags.
    Zachary Johnson on