【问题标题】:D3: Repositioning Pie Chart Labels on UpdateD3:更新时重新定位饼图标签
【发布时间】:2016-03-22 23:07:14
【问题描述】:

小提琴: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 的答案。

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 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('id', function(d, i) {
    return 'pie' + i;
  })
  .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('.arc')
  .data(function(d) {
    return pie(d.values);
  })
  .enter()
  .append('g')
  .attr('class', 'arc');

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.select('#pie' + x)
      .data(newdata);

    var npath = nslice.selectAll('path')
      .data(function(d) {
        return pie(d.values);
      })
      .attr('class', function(d) {
        return 'arc ' + d.data.platform;
      });

    npath.transition().duration(1000)
      .attrTween('d', arcTween);

    npath.exit()
      .remove();

    var ntext = nslice.selectAll('.label')
      .data(function(d) {
         return d.values;
       })
      .style('opacity', 0);

    ntext.transition().duration(1000)
      .style('opacity', 1)
      .text(function(d) {
        if (d.val > 0) {
          return d.val + '%';
        }
      })
      // .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();

【问题讨论】:

  • 这与您链接到的第二个问题完全相同。您没有更新绑定到 text 元素的数据。

标签: javascript d3.js charts


【解决方案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
  });

编辑

我的错,我应该抓住它。问题是您将.selectAll.data 与显式循环结合使用。 d3 数据绑定就是基于数据进行循环。那么,我们该如何解决呢?

我们从一开始就进行正确的数据绑定。

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>

【讨论】:

  • 谢谢!你是救生员。我没有意识到我在 change() 中选择的 .arc 是另一回事。难怪数据没有被正确继承。
  • 说得太早了。所有饼图都使用相同的数据进行更新,使它们完全相同。将进一步挖掘。
猜你喜欢
  • 1970-01-01
  • 2017-10-24
  • 2018-06-14
  • 2014-10-08
  • 1970-01-01
  • 1970-01-01
  • 2013-11-12
  • 2017-10-08
  • 1970-01-01
相关资源
最近更新 更多