Search code examples
d3.jsdonut-chart

D3 Donut transition, d,i getting undefine


        var arcMin = 75;        // inner radius of the first arc
        var arcWidth = 25;      // width
        var arcPad = 10;         // padding between arcs

        var arc = d3.arc()
                  .innerRadius(function(d, i) {
                    return  arcMin + i*(arcWidth) + arcPad;
                  })
                  .outerRadius(function(d, i) {
                    return arcMin + (i+1)*(arcWidth);
                  })
                  .startAngle(0 * (PI/180))
                  .endAngle(function(d, i) {
//                    console.log(d);       <----getting undefine under attrTween Call
                    return 2*PI*d.value/100;
                  });

        var path = g.selectAll('path')
          .data(pie(dataset))
          .enter()
          .append('path')
          .attr('d', arc)
          .attr('fill', function(d, i) { 
            return d.data.color;
          })
          .transition()
          .delay(function(d, i) {
            return i * 800;
          });
//          .attrTween('d', function(d) {
//            // This part make my chart disapear
//             var i = d3.interpolate(d.startAngle, d.endAngle);
//             return function(t) {
//               d.endAngle = i(t);
//               return arc(d);
//             }
//            // This part make my chart disapear
//          });

arc(d) always return "M0,0Z"..

I found that the reason is when calling arc under arcTween, all d,i return undefine. How can i solve this.

Codes here: https://jsfiddle.net/m8oupfne/3/

Final product:


Solution

  • Couple things:

    1. At first glance your attrTween function doesn't work because your arc function is dependent on both d,i and you only pass d to it.

    2. But, fixing that doesn't make your chart transition nicely? Why? Because your arc function doesn't seem to make any sense. You use pie to calculate angles and then overwrite them in your arc function. And each call to the arc function calculates endAngle the same since it's based on d.value.

    So, if you want a custom angle calculation, don't call pie at all, but pre-calculate your endAngle and don't do it in your arc function.

    arc becomes:

    var arc = d3.arc()
      .innerRadius(function(d, i) {
        return  arcMin + i*(arcWidth) + arcPad;
      })
      .outerRadius(function(d, i) {
        return arcMin + (i+1)*(arcWidth);
      });
    

    Pre-calculate the data:

    dataset.forEach(function(d,i){
      d.endAngle = 2*PI*d.value/100;
      d.startAngle = 0;
    });
    

    arcTween becomes:

    .attrTween('d', function(d,i) {
      var inter = d3.interpolate(d.startAngle, d.endAngle);             
      return function(t) {
        d.endAngle = inter(t);
        return arc(d,i);
      }
    });
    

    Running code:

     (function(d3) {
            'use strict';
    
     
            var dataset = [
              { label: 'a', value: 88, color : '#898989'},
              { label: 'b', value: 56 , color : '#898989'},
              { label: 'c', value: 20 , color : '#FDD000'},
              { label: 'd', value: 46 , color : '#898989'},
            ];
          
            var PI = Math.PI;
            var arcMin = 75;        // inner radius of the first arc
            var arcWidth = 25;      // width
            var arcPad = 10;         // padding between arcs
            var arcBgColor = "#DCDDDD";
          
            var width = 360;
            var height = 360;
            var radius = Math.min(width, height) / 2;
            var donutWidth = 15;                            // NEW
    
            var svg = d3.select('#canvas')
              .append('svg')
              .attr('width', width)
              .attr('height', height);
          
           var gBg = svg.append('g').attr('transform', 'translate(' + (width / 2) + 
                ',' + (height / 2) + ')');
           var g = svg.append('g')
              .attr('transform', 'translate(' + (width / 2) + 
                ',' + (height / 2) + ')');
    				
            var arc = d3.arc()
                      .innerRadius(function(d, i) {
                        return  arcMin + i*(arcWidth) + arcPad;
                      })
                      .outerRadius(function(d, i) {
                        return arcMin + (i+1)*(arcWidth);
                      });
    
            var arcBg = d3.arc()
                      .innerRadius(function(d, i) {
                        return  arcMin + i*(arcWidth) + arcPad;
                      })
                      .outerRadius(function(d, i) {
                        return arcMin + (i+1)*(arcWidth);
                      })
                      .startAngle(0 * (PI/180))
                      .endAngle(function(d, i) {
                        return 2*PI;
                      });
    			
            var pie = d3.pie()
              .value(function(d) { return d.value; })
              .sort(null);
    
          
            var pathBg = gBg.selectAll('path')
              .data(pie(dataset))
              .enter()
              .append('path')
              .attr('d', arcBg)
              .attr('fill', arcBgColor );
              
            dataset.forEach(function(d,i){
            	d.endAngle = 2*PI*d.value/100;
              d.startAngle = 0;
            });
            
            var path = g.selectAll('path')
              .data(dataset)
              .enter()
              .append('path')
              .attr('fill', function(d, i) { 
                return d.color;
              })
              .transition()
              .duration(800)
              .delay(function(d, i) {
               return i * 800;
              })
              .attrTween('d', function(d,i) {
                 var inter = d3.interpolate(d.startAngle, d.endAngle);             
                 return function(t) {
                   d.endAngle = inter(t);
                   return arc(d,i);
                 }
              });
    		
          })(window.d3);
    			
    <script src="https://cdn.jsdelivr.net/jquery/2.1.4/jquery.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/d3js/4.6.0/d3.min.js"></script>
       <div id="canvas"></div>