After 15 years doing UI development, there\'s very little I look at and think, \"how on earth do I do that.\" This is one of those times.
A graphic designer has sold
If it absolutely must be as compatible as possible, I would generate an SVG image and render it to PNG. This is not nearly as slow as it sounds for an image like this with so few points.
Here's a very quick, very dirty example. It assumes that you have the ImageMagick extension available though in a pinch you could dump the SVG to a file and exec()
a command-line tool like rsvg. Obviously the "right" answer involves some sort of caching scheme for the rendered graph. Also, forgive me for not being more of a SVG ninja.
graph.svg.php:
<?php echo '<'; ?>?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="240" height="240"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="translate(120,120) translate(0,15)">
<polygon fill="none" stroke="green" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['green']; ?>)">
<polygon fill="green" stroke="green" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(72) translate(0,15)">
<polygon fill="none" stroke="red" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['red']; ?>)">
<polygon fill="red" stroke="red" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(144) translate(0,15)">
<polygon fill="none" stroke="yellow" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['yellow']; ?>)">
<polygon fill="yellow" stroke="yellow" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(216) translate(0,15)">
<polygon fill="none" stroke="purple" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['purple']; ?>)">
<polygon fill="purple" stroke="purple" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
<g transform="translate(120,120) rotate(288) translate(0,15)">
<polygon fill="none" stroke="blue" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
<g transform="scale(<?php echo $fill['blue']; ?>)">
<polygon fill="blue" stroke="blue" stroke-width="10"
points="0,0 -50,68.82 50,68.82" />
</g>
</g>
</svg>
render.php:
<?php
$fill = array(
'green' => 0.5,
'red' => 0.8,
'yellow' => 0.55,
'purple' => 0.4,
'blue' => 0.75,
);
ob_start();
include('graph.svg.php');
$svg = ob_get_contents();
ob_end_clean();
$im = new Imagick();
$im->readImageBlob($svg);
$im->setImageFormat('png24');
$png = $im->getImagesBlob();
$im->clear();
$im->destroy();
header('Content-Type: image/png');
header('Content-Length: ' . strlen($png));
echo $png;
exit;
Output looks like this:
What about <canvas>
?
You can easily draw one triangle and then draw the others by just rotating the canvas 360/5
degrees.
Example: http://jsfiddle.net/Stijntjhe/dC6kX/
window.onload = function() {
var ce = document.getElementById('ce');
var c = ce.getContext('2d');
c.translate(ce.offsetWidth / 2, ce.offsetHeight / 2);
for(var pie = 0; pie < 5; pie++) {
c.save();
c.rotate(pie/5 * Math.PI * 2);
c.beginPath();
c.moveTo(0, -10);
c.lineTo(-50, -80);
c.lineTo(50, -80);
c.lineTo(0, -10);
c.lineWidth = 5;
c.lineCap = 'square';
c.strokeStyle = colors[pie];
c.stroke();
c.restore();
}
}
Becomes:
Cons: Maybe not cross-browser yet.
Unless I could find an implementation already written, I'd use Raphaël.
It will take significant work, but the end result should be very good.
Take a look at some of the demos, they're incredibly slick.
Raphaël currently supports Firefox 3.0+, Safari 3.0+, Chrome 5.0+, Opera 9.5+ and Internet Explorer 6.0+.
This seemed interesting, so I decided to implement it myself with Raphaël:
See: http://jsfiddle.net/2Tsjy/
It should work in "all browsers". The only part I didn't do was the text.
JavaScript:
var paper = Raphael("pentagon"),
fullNum = [40, 53],
borderColours = ['#329342','#9e202c','#f47933','#811f5a','#11496c'],
fillColours = ['#74ae3d','#d01f27','#eaa337','#32133f','#2c7aa1'],
triangles = [],
border, fill, st, i;
for (i=0; i<5; i++) {
border = paper.path(getPercentPath(0)).attr({
'fill': borderColours[i],
'stroke-width': 0
}),
fill = paper.path(["M", 116, 123] + "l-44,61 88,0z").attr({
'stroke': fillColours[i],
'stroke-width': 6
});
triangles.push(border);
st = paper.set();
st.push(border, fill);
st.rotate(i * 72, 116, 113);
setPercent(i, 30+Math.floor(Math.random()*70));
}
function getPercentPath(percent) {
var ratio = percent/100;
return ["M", 116, 128] + "l-" + ratio*fullNum[0] + "," + ratio*fullNum[1] + " " + ratio*fullNum[0]*2 + ",0z";
}
function setPercent(i, percent) {
triangles[i].attr({
path: getPercentPath(percent)
});
}
setInterval(function(){
for (var i=0; i<5; i++) {
setPercent(i, 30+Math.floor(Math.random()*70));
}
}, 2000);
CSS:
#pentagon {
width: 226px;
height: 227px;
border: 1px solid red;
background: #fff;
background: rgba(255,255,255,0.8)
}
HTML:
<div id="pentagon"></div>
I'd have to recommend RaphaelJS (see http://raphaeljs.com/). It is IE7 compatible, and you can do the triangles just fine: you need to do the math, but quite possible.
EDIT: look at http://www.chittram.com/editor.jsp for a quick sample of some of the shapes that can be done. That site is an interactive editor but the core capabilities you need are demonstrated.
I think if you have to do it in JS/CSS and Flash/HTML5 isn't an option, take a look at a handy trick of using triangles in CSS:
http://www.howtocreate.co.uk/tutorials/css/slopes
And an alternative reference:
http://css-tricks.com/snippets/css/css-triangle/
By setting the border thickness's of boxes cleverly, you can get any shape triangle you want, at any rotation. It's tricky to figure out, and I don't have code handy but it is possible.
You could nest triangles within each other (looking the sample picture I note it is composed entirely of triangles, the inner triangle is an inverted nest of the outer triangle) so I think it's perfectly possible, although that maths might get a bit tricky in regards to positioning and if you need the chart to be flexible (arbitrary numbers of triangles and sizes).