D3:在更新时重新定位饼图标签

时间:2015-12-16 17:27:07

标签: javascript d3.js charts

小提琴:https://jsfiddle.net/vpkarep8/

我有三个饼图在使用新数据进行更新时动画,我似乎无法让标签正确更新。上面附有一个小提琴。

要使文本更改,我必须在文本上进行另一个数据连接(第486-489行),但后来我无法使用arc.centroid()。将其缩小到我如何处理更新,但不知道处理所有这些的最佳方法。似乎质心需要d,但要更新文本需要d.values。

有什么想法吗?

尝试了Label outside arc (Pie chart) d3.jsHow to update both the content and location of text labels on a D3 pie chart的答案。

likeButton.transform = CGAffineTransformMakeScale(2, 2) //doubles the button's size

1 个答案:

答案 0 :(得分:1)

首先,不要在两个不同的对象上重用类名arc;您在每个切片的父g和子path上都有它。

其次,将数据重新绑定到父g,以便pathtext都可以使用它。

 var slice = svg.selectAll('.slice') //<-- for the g
   .data(function(d) {
     return pie(d.values);
   })
   .enter()
   .append('g')
   .attr('class', 'slice');

稍后更新:

var npath = nslice.selectAll('.slice') //<-- rebind to `g`
  .data(function(d) {
    return pie(d.values);
  });

npath
  .select("path")
  .attr('class', function(d) {
    return 'arc ' + d.data.platform;
  })
  .transition().duration(1000)
  .attrTween('d', arcTween); //<-- update the paths

npath.exit()
  .remove(); //<-- remove the whole g

npath.select("text") //<-- update the text
  .transition()
  .duration(1000)
  .style('opacity', 1)
  .text(function(d) {
    if (d.data.val > 0) {
      return d.data.val + '%';
    }
  })
  .attr('transform', function(d) {
    console.log(arc.centroid(d)); //<-- you can now use centroid
  });

<强> EDITS

我的坏,我应该抓住了。问题是您将.selectAll.data与显式循环相结合。 d3数据绑定是关于它根据数据进行循环的全部内容。那么,我们该如何解决呢?

我们从get go开始正确的数据绑定。

var svg = d3.select('.ES__graph__container')
  .selectAll('svg')
  .data(brandDataByYear)
  .enter()
  .append('svg')
  .style('margin-top', '25px')
  .attr('width', (r + m) * 2)
  .attr('height', (r + m) * 2)
  .attr('class', 'pie') //<-- give each svg a class, not id
  ...

然后在您的更新中:

// for (x in newdata) { //<-- NO EXPLICIT LOOPING!
var nslice = d3.selectAll('.pie')
  .data(newdata);

更新完整代码:

<!DOCTYPE html>
<html>

<head>
  <script data-require="jquery@2.1.4" data-semver="2.1.4" src="https://code.jquery.com/jquery-2.1.4.js"></script>
  <script src="//d3js.org/d3.v3.js" charset="utf-8"></script>
  <style>
    .arc.platform1 {
      fill: #e74341;
    }
    
    .arc.platform2 {
      fill: #3c5a96;
    }
    
    .arc.platform3 {
      fill: #3c94d1;
    }
    
    .arc.platform4 {
      fill: #837369;
    }
  </style>
</head>

<body>
  <div class="ES__buttons"></div>
  <div class="ES__graph__container"></div>
  <script>
    function drawESGraph() {
      d3.selectAll('.ES__graph__container svg')
        .remove();

      d3.selectAll('.ES__buttons button')
        .remove();

      var $container = $('.ES__graph__container');

      var width = $container.width() / 3;

      var m = 40,
        r = width / 3,
        labelr = r + 20;

      var arc = d3.svg.arc()
        .outerRadius(r)
        .innerRadius(r / 2);

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

      var data = [{
        brand: 'brand1',
        platform: 'platform1',
        year: '2012-2013',
        val: 85.8
      }, {
        brand: 'brand1',
        platform: 'platform2',
        year: '2012-2013',
        val: 14
      }, {
        brand: 'brand1',
        platform: 'platform3',
        year: '2012-2013',
        val: 0.2
      }, {
        brand: 'brand1',
        platform: 'platform4',
        year: '2012-2013',
        val: 0
      }, {
        brand: 'brand1',
        platform: 'platform1',
        year: '2013-2014',
        val: 91
      }, {
        brand: 'brand1',
        platform: 'platform2',
        year: '2013-2014',
        val: 8
      }, {
        brand: 'brand1',
        platform: 'platform3',
        year: '2013-2014',
        val: 1
      }, {
        brand: 'brand1',
        platform: 'platform4',
        year: '2013-2014',
        val: 0
      }, {
        brand: 'brand1',
        platform: 'platform1',
        year: '2014-2015',
        val: 77
      }, {
        brand: 'brand1',
        platform: 'platform2',
        year: '2014-2015',
        val: 8
      }, {
        brand: 'brand1',
        platform: 'platform3',
        year: '2014-2015',
        val: 2
      }, {
        brand: 'brand1',
        platform: 'platform4',
        year: '2014-2015',
        val: 13
      }, {
        brand: 'brand2',
        platform: 'platform1',
        year: '2012-2013',
        val: 76.9
      }, {
        brand: 'brand2',
        platform: 'platform2',
        year: '2012-2013',
        val: 23
      }, {
        brand: 'brand2',
        platform: 'platform3',
        year: '2012-2013',
        val: 0.1
      }, {
        brand: 'brand2',
        platform: 'platform4',
        year: '2012-2013',
        val: 0
      }, {
        brand: 'brand2',
        platform: 'platform1',
        year: '2013-2014',
        val: 87.6
      }, {
        brand: 'brand2',
        platform: 'platform2',
        year: '2013-2014',
        val: 7
      }, {
        brand: 'brand2',
        platform: 'platform3',
        year: '2013-2014',
        val: 0.4
      }, {
        brand: 'brand2',
        platform: 'platform4',
        year: '2013-2014',
        val: 5
      }, {
        brand: 'brand2',
        platform: 'platform1',
        year: '2014-2015',
        val: 55
      }, {
        brand: 'brand2',
        platform: 'platform2',
        year: '2014-2015',
        val: 7
      }, {
        brand: 'brand2',
        platform: 'platform3',
        year: '2014-2015',
        val: 1
      }, {
        brand: 'brand2',
        platform: 'platform4',
        year: '2014-2015',
        val: 37
      }, {
        brand: 'brand3',
        platform: 'platform1',
        year: '2012-2013',
        val: 72.9
      }, {
        brand: 'brand3',
        platform: 'platform2',
        year: '2012-2013',
        val: 24
      }, {
        brand: 'brand3',
        platform: 'platform3',
        year: '2012-2013',
        val: 0.1
      }, {
        brand: 'brand3',
        platform: 'platform4',
        year: '2012-2013',
        val: 3
      }, {
        brand: 'brand3',
        platform: 'platform1',
        year: '2013-2014',
        val: 76
      }, {
        brand: 'brand3',
        platform: 'platform2',
        year: '2013-2014',
        val: 10
      }, {
        brand: 'brand3',
        platform: 'platform3',
        year: '2013-2014',
        val: 1
      }, {
        brand: 'brand3',
        platform: 'platform4',
        year: '2013-2014',
        val: 13
      }, {
        brand: 'brand3',
        platform: 'platform1',
        year: '2014-2015',
        val: 56
      }, {
        brand: 'brand3',
        platform: 'platform2',
        year: '2014-2015',
        val: 12
      }, {
        brand: 'brand3',
        platform: 'platform3',
        year: '2014-2015',
        val: 1
      }, {
        brand: 'brand3',
        platform: 'platform4',
        year: '2014-2015',
        val: 31
      }, {
        brand: 'brand4',
        platform: 'platform1',
        year: '2012-2013',
        val: 1
      }, {
        brand: 'brand4',
        platform: 'platform2',
        year: '2012-2013',
        val: 63
      }, {
        brand: 'brand4',
        platform: 'platform3',
        year: '2012-2013',
        val: 1
      }, {
        brand: 'brand4',
        platform: 'platform4',
        year: '2012-2013',
        val: 35
      }, {
        brand: 'brand4',
        platform: 'platform1',
        year: '2013-2014',
        val: 0
      }, {
        brand: 'brand4',
        platform: 'platform2',
        year: '2013-2014',
        val: 22
      }, {
        brand: 'brand4',
        platform: 'platform3',
        year: '2013-2014',
        val: 1
      }, {
        brand: 'brand4',
        platform: 'platform4',
        year: '2013-2014',
        val: 77
      }, {
        brand: 'brand4',
        platform: 'platform1',
        year: '2014-2015',
        val: 0
      }, {
        brand: 'brand4',
        platform: 'platform2',
        year: '2014-2015',
        val: 14
      }, {
        brand: 'brand4',
        platform: 'platform3',
        year: '2014-2015',
        val: 1
      }, {
        brand: 'brand4',
        platform: 'platform4',
        year: '2014-2015',
        val: 85
      }]

      var allBrands = d3.set(data.map(function(d) {
        return d.brand;
      })).values();

      var buttons = d3.select('.ES__buttons')
        .selectAll('button')
        .data(allBrands)
        .enter()
        .append('button')
        .attr('class', function(d) {
          return d + ' button';
        })
        .text(function(d) {
          return d;
        })
        .on('click', function(d) {
          updateChart(d);
        })
        .style('opacity', 0);

      buttons.transition().duration(1000)
        .style('opacity', 1);

      d3.select('.brand1.button')
        .attr('class', 'brand1 button active');

      function updateChart(brand) {
        var brandData = data.filter(function(d) {
          return d.brand === brand;
        });

        var brandDataByYear = d3.nest()
          .key(function(d) {
            return d.year;
          })
          .entries(brandData);

        var svg = d3.select('.ES__graph__container')
          .selectAll('svg')
          .data(brandDataByYear)
          .enter()
          .append('svg')
          .style('margin-top', '25px')
          .attr('width', (r + m) * 2)
          .attr('height', (r + m) * 2)
          .attr('class', 'pie')
          .append('svg:g')
          .attr('transform', 'translate(' + (r + m) + ',' + (r + m) + ')');

        var pieLabel = svg.append('svg:text')
          .attr('dy', '.35em')
          .attr('text-anchor', 'middle')
          .text(function(d) {
            return d.key;
          })
          .style('fill', 'black')
          .style('opacity', 0);

        pieLabel.transition().duration(1000)
          .style('opacity', 1);

        var slice = svg.selectAll('.slice')
          .data(function(d) {
            return pie(d.values);
          })
          .enter()
          .append('g')
          .attr('class', 'slice');

        var path = slice.append('svg:path')
          .attr('d', arc)
          .attr('class', function(d) {
            return 'arc ' + d.data.platform;
          })
          .each(function(d) {
            this._current = d;
          });

        var text = slice.append('text')
          .text(function(d) {
            if (d.data.val > 0) {
              return d.data.val + '%';
            }
          })
          .attr('transform', function(d) {
            if (d.data.val > 3) {
              return 'translate(' + arc.centroid(d) + ')';
            } else {
              var c = arc.centroid(d),
                x = c[0],
                y = c[1],
                h = Math.sqrt(x * x + y * y);

              return 'translate(' + (x / h * labelr) + ',' + (y / h * labelr) + ')';
            }
          })
          .attr('text-anchor', function(d) {
            if (d.data.val < 3) {
              return (d.endAngle + d.startAngle) / 2 > Math.PI ? 'end' : 'start';
            }
          })
          .attr('dx', function(d) {
            return d.data.val > 3 ? -15 : 18;
          })
          .attr('dy', function(d) {
            return d.data.val > 3 ? 5 : 3;
          })
          .style('fill', function(d) {
            return d.data.val > 3 ? 'white' : 'black';
          })
          .attr('class', 'label');

        change();

        function change() {
          
          var newdata = brandDataByYear;
          
         // for (x in newdata) {
            var nslice = d3.selectAll('.pie')
              .data(newdata);

          //return;

            var npath = nslice.selectAll('.slice')
              .data(function(d) {
                console.log(d);
                return pie(d.values);
              });
              
            npath
              .select("path")
              .attr('class', function(d) {
                return 'arc ' + d.data.platform;
              })
              .transition().duration(1000)
              .attrTween('d', arcTween);

            npath.exit()
              .remove();

            npath.select("text")
              .transition()
              .duration(1000)
              .style('opacity', 1)
              .text(function(d) {
                if (d.data.val > 0) {
                  return d.data.val + '%';
                }
              })
              .attr('transform', function(d) {
                if (d.data.val > 3) {
                  return 'translate(' + arc.centroid(d) + ')';
                } else {
                  var c = arc.centroid(d),
                    x = c[0],
                    y = c[1],
                    h = Math.sqrt(x * x + y * y);

                  return 'translate(' + (x / h * labelr) + ',' + (y / h * labelr) + ')';
                }
              });
              // .attr("transform", function(d) {
              //   return "translate(" + 
              //     ( (radius - 12) * Math.sin( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
              //     ", " +
              //     ( -1 * (radius - 12) * Math.cos( ((d.endAngle - d.startAngle) / 2) + d.startAngle ) ) +
              //   ")";
              //  })
              // .style("text-anchor", function(d) {
              //    var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle;
              //    if ( (rads > 7 * Math.PI / 4 && rads < Math.PI / 4) || (rads > 3 * Math.PI / 4 && rads < 5 * Math.PI / 4) ) {
              //      return "middle";
              //    } else if (rads >= Math.PI / 4 && rads <= 3 * Math.PI / 4) {
              //      return "start";
              //    } else if (rads >= 5 * Math.PI / 4 && rads <= 7 * Math.PI / 4) {
              //      return "end";
              //    } else {
              //      return "middle";
              //    }
              //  })

            // ntext.exit()
            //      .remove();
          }
      //  }

        function arcTween(a) {
          var i = d3.interpolate(this._current, a);

          this._current = i(0);

          return function(t) {
            return arc(i(t));
          }
        }
      }

      updateChart('brand1');
    }

    drawESGraph();
  </script>
</body>

</html>