Reputation: 8663
I am using angular and d3 to create a donut (in a directive).
I can quite simply give the filled area a colour (in this plunker it is blue). But what i want to do is have the SVG change its colours smoothly from:
0% - 33.3% - red
33.4% - 66.66% - orange
66.7% - 100% green
Directive:
app.directive('donutDirective', function() {
return {
restrict: 'E',
scope: {
radius: '=',
percent: '=',
text: '=',
},
link: function(scope, element, attrs) {
var radius = scope.radius,
percent = scope.percent,
percentLabel = scope.text,
format = d3.format(".0%"),
progress = 0;
var svg = d3.select(element[0])
.append('svg')
.style('width', radius/2+'px')
.style('height', radius/2+'px');
var donutScale = d3.scale.linear()
.domain([0, 100])
.range([0, 2 * Math.PI]);
//var color = "#5599aa";
var color = "#018BBB";
var data = [
[0,100,"#b8b5b8"],
[0,0,color]
];
var arc = d3.svg.arc()
.innerRadius(radius/6)
.outerRadius(radius/4)
.startAngle(function(d){return donutScale(d[0]);})
.endAngle(function(d){return donutScale(d[1]);});
var text = svg.append("text")
.attr("x",radius/4)
.attr("y",radius/4)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.attr("font-size","14px")
.style("fill","black")
.attr("text-anchor", "middle")
.text(percentLabel);
var path = svg.selectAll("path")
.data(data)
.enter()
.append("path")
.style("fill", function(d){return d[2];})
.attr("d", arc)
.each(function(d) {
this._current = d;
// console.log(this._current)
;});
// update the data!
data = [
[0,100,"#b8b5b8"],
[0,percent,color]
];
path
.data(data)
.attr("transform", "translate("+radius/4+","+radius/4+")")
.transition(200).duration(2150).ease('linear')
.attrTween("d", function (a) {
var i = d3.interpolate(this._current, a);
var i2 = d3.interpolate(progress, percent)
this._current = i(0);
// console.log(this._current);
return function(t) {
text.text( format(i2(t) / 100) );
return arc(i(t));
};
});
}
};
});
Plunker: http://plnkr.co/edit/8qGMeQkmM08CZxZIVRei?p=preview
Upvotes: 4
Views: 1026
Reputation: 961
i want to do is have the SVG change its colours smoothly from:
0% - 33.3% - red 33.4% - 66.66% - orange 66.7% - 100% green
Assuming that you want a color transition/scale like this one:
See working code for this: http://codepen.io/anon/pen/vLVmyV
You can smothly make the color transition using a d3 linear scale like this:
//Create a color Scale to smothly change the color of the donut
var colorScale = d3.scale.linear().domain([0,33.3,66.66,100]).range(['#cc0000','#ffa500','#ffa500','#00cc00']);
Then, when you update the path (with the attrTween) to make the filling animation, take only the Path the represents the filled part of the donut, lets call it colorPath and change the fill of it adding the following like in the tween:
//Set the color to the path depending on its percentage
//using the colorScale we just created before
colorPath.style('fill',colorScale(i2(t)))
Your attrTween will look like this:
colorPath.data([[0,percent,color]])
.transition(200).duration(2150).ease('linear')
.attrTween("d", function (a) {
var i = d3.interpolate(this._current, a);
var i2 = d3.interpolate(progress, percent)
this._current = i(0);
// console.log(this._current);
return function(t) {
text.text( format(i2(t) / 100) );
colorPath.style('fill',colorScale(i2(t)))
return arc(i(t));
};
});
Please note that we only update the data for the colorPath: colorPath.data([[0,percent,color]])
The whole working example is right here: http://plnkr.co/edit/ox82vGxhcaoXJpVpUel1?p=preview
Upvotes: 2
Reputation: 32327
First give Id to the path like this:
var path = svg.selectAll("path")
.data(data)
.enter()
.append("path")
.style("fill", function(d){return d[2];})
.attr("d", arc)
.attr("id", function(d,i){return "id"+i;})//give id
Then inside the tween pass the condition and change the color of the path
.attrTween("d", function (a) {
var i = d3.interpolate(this._current, a);
var i2 = d3.interpolate(progress, percent)
this._current = i(0);
return function(t) {
if(i2(t) < 33.3)
d3.selectAll("#id1").style("fill", "red")
else if(i2(t) < 66.6)
d3.selectAll("#id1").style("fill", "orange")
else if(i2(t) > 66.6)
d3.selectAll("#id1").style("fill", "green")
text.text( format(i2(t) / 100) );
return arc(i(t));
};
});
Working code here
EDIT
Inside your directive you can make gradient
inside your defs
like this:
var defs = svg.append("defs");
var gradient1 = defs.append("linearGradient").attr("id", "gradient1");
gradient1.append("stop").attr("offset", "0%").attr("stop-color", "red");
gradient1.append("stop").attr("offset", "25%").attr("stop-color", "orange");
gradient1.append("stop").attr("offset", "75%").attr("stop-color", "green");
Then in the path you can define the gradient like this:
var path = svg.selectAll("path")
.data(data)
.enter()
.append("path")
.style("fill", function(d, i) {
if (i == 0) {
return d[2];
} else {
return "url(#gradient1)";
}
})
Working code here
Hope this helps!
Upvotes: 3