Angular svg or canvas to use colour gradients

2019-06-25 08:45发布

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

2条回答
smile是对你的礼貌
2楼-- · 2019-06-25 09:18

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:

enter image description here

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

查看更多
一夜七次
3楼-- · 2019-06-25 09:29

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!

查看更多
登录 后发表回答