arc.centroid returning (NaN, NaN) in D3

2020-04-15 16:06发布

问题:

Fair warning: I'm a D3 rookie here. I'm building a donut chart using D3 and all is well so far, except that the labels on the slices aren't aligning with the slices. Using the code below, the labels for each slice are rendered in the middle of the chart, stacked on top of each other so they're unreadable. I've dropped the arc.centroid in my transform attribute, but it's returning "NaN,NaN" instead of actual coordinates, and I can't understand where it's reading from that it's not finding a number. My innerRadius and outerRadius are defined in the arc variable. Any help?

(pardon the lack of a jsfiddle but I'm pulling data from a .csv here)

var width = 300,
    height = 300,
    radius = Math.min(width, height) / 2;

var color = ["#f68b1f", "#39b54a", "#2772b2"];

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

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

var svg = d3.select("#pieplate").append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

d3.csv("data.csv", type, function(error, data) {
  var path = svg.datum(data).selectAll("path")
      .data(pie)
    .enter().append("path")
      .attr("fill", function(d, i) { return color[i]; })
      .attr("d", arc)
      .each(function(d) { this._current = d; }); // store the initial angles

  var text = svg.selectAll("text")
                        .data(data)
                        .enter()
                        .append("text")
                        .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
                        .attr("dy", ".35em")
                        .attr("text-anchor", "middle")
                        .text( function (d) { return d.taskforce1; })
                        .attr("font-family", "sans-serif")
                        .attr("font-size", "20px")
                        .attr("fill", "black");

  d3.selectAll("a")
      .on("click", switcher);

  function switcher() {
    var value = this.id;
    var j = value + 1;
    pie.value(function(d) { return d[value]; }); // change the value function
    path = path.data(pie); // compute the new angles
    path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
    textLabels = text.text( function (d) { return d[value]; });
  }
});

function type(d) {
  d.taskforce1 = +d.taskforce1;
  d.taskforce2 = +d.taskforce2;
  d.taskforce3 = +d.taskforce3;
  return d;
}

// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
  var i = d3.interpolate(this._current, a);
  this._current = i(0);
  return function(t) {
    return arc(i(t));
  };
}

回答1:

Finally got it. The arc.centroid function expects data with precomputed startAngle and endAngle which is the result of pie(data). So the following helped me:

var text = svg.selectAll("text")
                    .data(pie(data))

followed by the rest of the calls. Note that you might have to change the way to access the text data that you want to display. You can always check it with

// while adding the text elements
    .text(function(d){ console.log(d); return d.data.textAttribute })