Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

D3: gauge chart with growing arc

I want to achieve something like a growing arc which indicates 5 levels (see picture). My data has only an integer value which is between 1-5. You can ignore the icon in the middle for now. Is there any possibility to achieve something like that in d3? I couldn't find any example for this. Moreover I tried it with a cut off pie (donut) chart approach, but I couldn't make the growing arc... I would appreciate any help! Thanks for that.

enter image description hereenter image description here

like image 588
Robin Wieruch Avatar asked May 10 '15 19:05

Robin Wieruch


1 Answers

You can do this with d3 without dependency on external images, SVG sprites or anything in the DOM — just d3.js.

Here's a working fiddle. The implementation is explained below. But also, here's a more advanced fiddle that animates a clip-path over the growing arc. Check out its predecessor to see how the mask looks without clipping.

First, you need to represent the graphics as an array of data that you bind to with d3. Specifically, you need a color and a "line command" (the string you assign to d as in <path d="...">. Something like this:

var segmentData = [
    { color:"#ED6000", cmd:"M42.6,115.3c5.2,1.5,11,2.4,16.8,2.4c1.1,0,2.7,0,3.7-0.1v-2.2c-7,0-13.1-1.3-18.8-3.6L42.6,115.3z" },
    { color:"#EF7D00", cmd:"M25.7,99.3c4.3,4.7,9.5,8.6,15.3,11.3l-1.4,3.8c-6.9-2.4-13.2-6.1-18.6-10.8L25.7,99.3z" },
    { color:"#F4A300", cmd:"M23.7,97c-5.2-6.4-8.8-14-10.3-22.4L2.9,75.7c2.9,10,8.5,18.9,15.8,25.9L23.7,97z" },
    { color:"#F7BC00", cmd:"M13,71.5c-0.2-2-0.4-4-0.4-6c0-10.7,3.4-20.6,9.2-28.8L9.4,28.3c-5.6,9-8.9,19.6-8.9,30.9  c0,4.6,0.6,9.1,1.6,13.5L13,71.5z" },
    { color:"#FFCF36", cmd:"M63,15.7V0.8c-1-0.1-2.5-0.1-3.7-0.1c-19.9,0-37.5,9.9-48.1,25l12.7,8.6C33.1,23,46,15.7,63,15.7z" }
];

Then you need an empty <svg> and probably a <g> within it, into which to draw the graphics:

var svg = d3.select("body").append("svg")
    .attr("width", 125)
    .attr("height", 125);

var gauge = svg.append("g");

Then you use d3 binding to create the segments:

var segments = gauge.selectAll(".segment")
    .data(segmentData);
segments.enter()
    .append("path")
    .attr("fill", function(d) { return d.color; })
    .attr("d", function(d) { return d.cmd; });

This just creates the graphic, but doesn't color it based on an integer value. For that, you can define an update function:

function update(value) {
    segments
        .transition()
        .attr("fill", function(d, i) {
            return i < value ? d.color : "#ccc";
        })
}

Calling update(4) will color all but the last segment. Calling update(0) color none (leaving all of them gray).

In the fiddle, there's also a tick() function that calls update with a new value on a setTimeout basis, but that's just for demo.

Finally, if you wish, you can wrap all that code up and create a reusable component by following the advice in [this article].(http://bost.ocks.org/mike/chart/)

like image 132
meetamit Avatar answered Sep 19 '22 07:09

meetamit