Reputation: 1214
I am using d3.js to display a circle progress graph. It's working great but I'd like to show a specific percentage range on the graph in a different color. I'd like to show 75% - 90% as a different color on the graph. How can I achieve this? I looked at the donut chart to accomplish this but I like that the circle graph is animated so I'd like to stick with it and modify it to achieve my needs.
Goal:
Add 75%-90% percentage range in a different line color on the graph. The graph exists to show a 75%-90% "accuracy rating".
Bonus:
Add "75% to 90%" label like shown in this image:
JS:
var colors = {
'pink': '#ffffff',
'yellow': '#f0ff08',
'green': '#47e495'
};
var color = colors.pink;
var line_two_color = colors.green;
var radius = 50;
var border = 3;
var padding = 10;
var startPercent = 0;
var endPercent = 0.90;
var twoPi = Math.PI * 2;
var formatPercent = d3.format('.0%');
var boxSize = 130;
var count = Math.abs((endPercent - startPercent) / 0.01);
var step = endPercent < startPercent ? -0.01 : 0.01;
var arc = d3.svg.arc()
.startAngle(0)
.innerRadius(radius)
.outerRadius(radius - border);
var parent = d3.select('div#circle_graph');
var svg = parent.append('svg')
.attr('width', boxSize)
.attr('height', boxSize);
var defs = svg.append('defs');
var g = svg.append('g')
.attr('transform', 'translate(' + boxSize / 2 + ',' + boxSize / 2 + ')');
var meter = g.append('g')
.attr('class', 'progress-meter');
meter.append('path')
.attr('class', 'background')
.attr('fill', '#ccc')
.attr('fill-opacity', 0.5)
.attr('d', arc.endAngle(twoPi));
var foreground = meter.append('path')
.attr('class', 'foreground')
.attr('fill', color)
.attr('fill-opacity', 1)
.attr('stroke', color)
.attr('stroke-width', 5)
.attr('stroke-opacity', 1)
var front = meter.append('path')
.attr('class', 'foreground')
.attr('fill', color)
.attr('fill-opacity', 1);
var numberText = meter.append('text')
.attr('fill', '#fff')
.attr('text-anchor', 'middle')
.attr('dy', '.35em');
function updateProgress(progress) {
foreground.attr('d', arc.endAngle(twoPi * progress));
front.attr('d', arc.endAngle(twoPi * progress));
numberText.text(formatPercent(progress));
}
var progress = startPercent;
(function loops() {
updateProgress(progress);
if (count > 0) {
count--;
progress += step;
setTimeout(loops, 10);
}
})();
CSS:
.progress-meter text {
font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 24px;
font-weight: bold;
}
HTML:
<div id="circle_graph"></div>
Upvotes: 2
Views: 1536
Reputation: 3562
Hopefully I'm understanding your question correctly!
Also note, if your data is in % (rather than radians), you'll have to add a d3.scale to convert [0,100] domain to [0, 2pi].
The below code mimics a single progress arc with two separate arcs. One for the 0-75% range and a second for above 75%. Both arcs draw based on the same data but the key is using min and max functions to clamp the data as it passes the 75% threshold.
For the first bar, the end angle is stopped when the progress passes 75%...
.attr('d', function(d){
progressArc.startAngle(0)
return progressArc.endAngle( Math.min(d,(3/2)*Math.PI) )();
})
Math.min(d,(3/2)*Math.PI)
while for the second bar, the end angle only begins to change after the data passes 75%...
.attr('d', function(d){
progressArc.startAngle((3/2)*Math.PI)
return progressArc.endAngle( Math.max(d,(3/2)*Math.PI ))();
})
Math.max(d,(3/2)*Math.PI
The end result looks like one bar changing color as it passes a threshold.
var height = 20,
width = 70,
progress = 3;
d3.select('div').append('svg')
.attr('width','100%')
.attr('viewBox','0 0 ' + width + ' ' + height)
d3.select('svg').append('g')
.attr('transform','translate('+width/2+','+height/2+')')
.attr('id','main')
var progressArc = d3.svg.arc()
.innerRadius(7)
.outerRadius(9)
.startAngle(0)
.endAngle(2*Math.PI)
var progressBar1 = d3.select('#main').append('g').attr('class','progressBar1'),
progressBar2 = d3.select('#main').append('g').attr('class','progressBar2');
progressBar1.selectAll('path')
.data([progress])
.enter()
.append('path')
.attr('d', function(d){
progressArc.startAngle(0)
return progressArc.endAngle( Math.min(d,(3/2)*Math.PI) )();
})
progressBar2.selectAll('path')
.data([progress])
.enter()
.append('path')
.attr('fill','red')
.attr('d', function(d){
progressArc.startAngle((3/2)*Math.PI)
return progressArc.endAngle( Math.max(d,(3/2)*Math.PI ))();
})
var update = function(){
progress = progress >= 2*Math.PI ? 0 : progress + Math.random()*(1/200)*Math.PI;
console.log(progress)
progressBar1.selectAll('path')
.data([progress])
.attr('d', function(d){
progressArc.startAngle(0)
return progressArc.endAngle( Math.min(d,(3/2)*Math.PI) )();
})
progressBar2.selectAll('path')
.data([progress])
.attr('d', function(d){
progressArc.startAngle((3/2)*Math.PI)
return progressArc.endAngle( Math.max(d,(3/2)*Math.PI ))();
})
}
setInterval( update, 12);
svg{
border: solid green 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div></div>
Upvotes: 1