简体   繁体   中英

d3js scroll visibility - series animation for pie chart

在此处输入图片说明

I am working on a d3 applicaton - with a pie chart -- I would like to get animation onload and on a call to action. Like when the chart becomes visible during a scroll.

Where the pie segments grow around the central pivot. So tween or snap to the other segment like a relay race

http://jsfiddle.net/pg886/192/

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>

<div class="piechart" data-role="piechart" data-width=400 data-height=400 data-radius=30  data-innerradius=20
    data-data=x>
</div>

<style>
    .piechart{
      /*border: 1px solid black;*/  
      /*text-align: center;
      font-size: 12px;*/
    }
</style>

<script>

$( document ).ready(function() {
        console.log("test")

       var $this = $('.piechart');

        var data = [{
            "label": "Apples",
            "value": 100
        },
        {
            "label": "Pears",
            "value": 120
        },
        {
            "label": "Bananas",
            "value": 20
        }];


        var w = $this.data("width");
        var h = $this.data("height");
        var ir = $this.data("innerradius");
        var r = $this.data("radius");

        function colores_google(n) {
            var colores_g = ["#f7b363", "#448875", "#c12f39", "#2b2d39", "#f8dd2f"];
            //var colores_g = ["#47abd5", "#005a70", "#f5a0a3", "#ff7276", "#a9a19c", "#d0743c", "#ff8c00"];
            return colores_g[n % colores_g.length];
        }

        var radius = Math.min(w, h) / 4;


        var arc = d3.svg.arc()
            .outerRadius(radius - 10)
            .innerRadius(0);

        var labelArc = d3.svg.arc()
            .outerRadius(radius - r)
            .innerRadius(radius - ir);    

        var pie = d3.layout.pie()
            .sort(null)
            .value(function(d) { return d.value; });



        var chart = d3.select('.piechart').append("svg")
                        .attr("class", "chart")
                        .attr("width", w)
                        .attr("height", h)
                        .attr("transform", "translate(0,0)");

        var piechart = chart              
                        .append("g")
                        .attr("class", "piechart")
                        .attr("width", (radius*2))
                        .attr("transform", "translate(0,"+h/4+")");

        var path_group = piechart.append("g")
            .attr("class", "path_group")
            .attr("transform", "translate(90," + ((h / 4) - 20) + ")");


        var padding = 45;
        var legendPaddingTop = 30;
        var legend = chart.append("g")
            .attr("class", "legend")
            .attr("width", w/2)
            .attr("height", h)
            .attr("transform", "translate(" + (w - 50) + "," + (h / 4) + ")");  


        var label_group = legend.append("svg:g")
            .attr("class", "label_group")
            .attr("transform", "translate(" + (-(w / 3) + 20) + "," + 0 + ")");

        var legend_group = legend.append("svg:g")
            .attr("class", "legend_group")
            .attr("transform", "translate(" + (-(w / 3) - 100) + "," + 0 + ")");


        var g = path_group.selectAll(".arc")
            .data(pie(data))
            .enter().append("g")
            .attr("class", "arc");

        g.append("path")
            .attr("d", arc)
            .style("fill", function(d, i) { 
                return colores_google(i);
            });


        var legendHeight = legendPaddingTop;
        var ySpace = 18;

        //draw labels                   
        var labels = label_group.selectAll("text.labels")
            .data(data);

        labels.enter().append("svg:text")
            .attr("class", "labels")
            .attr("dy", function(d, i) {                  
                legendHeight+=ySpace;   
              return (ySpace * i) + 4;
            })
            .attr("text-anchor", function(d) {
              return "start";
            })
            .text(function(d) {
              return d.label;
            });

        labels.exit().remove();
        //draw labels


        //draw legend
        var legend = legend_group.selectAll("circle").data(data);

        legend.enter().append("svg:circle")
            .attr("cx", 100)
            .attr("cy", function(d, i) {
              return ySpace * i;
            })
            .attr("r", 7)
            .attr("width", 18)
            .attr("height", 18)
            .style("fill", function(d, i) {
              return colores_google(i);
            });

        legend.exit().remove();
        //draw legend

        //reset legend height
        //console.log("optimum height for legend", legendHeight);
        $this.find('.legend').attr("height", legendHeight);

        function type(d) {
          d.value = +d.value;
          return d;
        }

});

</script>

So you can achieve this pretty easily, and there are a couple of blocks that will help you.

Arc Tween Firstly this block gives you an example of how to tween an arc. Basically you can't get that automatically so you have to write your own attrTween function. Fortunately this is pretty simple and Mike Bostock gives a really good example in there.

Here's a code sample - but the link gives a really good verbose description of what's going on.

.attrTween("d", function(d) {
    var interpolate = d3.interpolate(d.endAngle, newAngle);    
    return function(t) {
      d.endAngle = interpolate(t);
      return arc(d);
    };
}   

Next you want something like this Donut with transitions . This is actually the end-result for where you're trying to get to. This effect is really easy to achieve, all you need to do is set your angles correctly and the timings.

Angles : So here you want both the endAngle and the startAngle to be the same at the start (which should be the endAngle value of the previous segment or 0 for the first segment).

Timing : You want to allow 1 animation to complete before you start the next, simply by delaying them. You can see how that's done with this snippet:

.transition()
.delay(function(d,i) { return i * 500; })
.duration(500)
.attrTween(...)

 const dataset = [ { age: "<5", population: 2704659 }, { age: "5-13", population: 4499890 }, { age: "14-17", population: 2159981 }, { age: "18-24", population: 3853788 }, { age: "25-44", population: 14106543 }, { age: "45-64", population: 8819342 }, { age: "≥65", population: 612463 }, ]; const TIME = 2000 / dataset.length; const color = d3.scaleOrdinal(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]); const pie = d3.pie() .sort(null) .value(function(d) { return d.population; }); const path = d3.arc() .innerRadius(0) .outerRadius(350); d3.select("#container") .selectAll(".arc") .data(pie(dataset)) .enter() .append("g") .attr("class", "arc") .append("path") .attr("fill", function(d) { return color(d.data.age); }) .transition() .duration(TIME) .ease(d3.easeLinear) .delay(function(d, i) { return i * TIME; }) .attrTween("d", function(d) { // Note the 0.1 to prevent errors generating the path const angleInterpolation = d3.interpolate(d.startAngle + 0.1, d.endAngle); return function(t) { d.endAngle = angleInterpolation(t); return path(d); } }); 
 <script src="https://d3js.org/d3.v4.min.js"></script> <svg width="800" height="800"> <g id="container" transform="translate(400, 400)"></g> </svg> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM