samedi 7 mai 2016

d3.js Adding elements to pie chart

I'm trying to update a pie chart with new data that can have more or less elements to show and new values. Removing arcs and updating values (with the same number of elements) is OK.

The problem is when adding new arcs: the pie transitions but it leaves blanks where the new arcs should be and draws some arcs outside the pie.

Here is how I create the pie (ommitting arcTween function, mouseovers, clicks, etc.)

    svg.attr('width', width).attr('height', height);

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

    let arc = d3.svg.arc()
            .innerRadius(innRadius)
            .outerRadius(outRadius);

    svg.append('g')
         .attr('class', 'donut-chart')
         .attr('transform', 'translate(' + (width / 2) + ',' + (height / 1.75) +')');

    svg.select('.donut-chart')
         .selectAll('.donut-arc')
         .data(pie(values))
         .enter()
         .append('g')
         .attr('class', 'donut-arc _selected_');

    svg.selectAll('.donut-arc')
         .append('path')
         .attr('fill', function(d, i) { 
            if ( scale === 'linear') return color(d.value);
            return color(labels[i]); 
         })
         .attr('d', arc)
         .each(function(d) { this._current = d; });

    svg.selectAll('.donut-arc')
        .append('text')
        .attr('class', 'labelText')
        .attr("x", function(d) {
          var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
          d.cx = Math.cos(a) * (outRadius + 75);
         return d.x = Math.cos(a) * (outRadius + 30);
        })
      .attr("y", function(d) {
          var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
          d.cy = Math.sin(a) * (outRadius + 75);
          return d.y = Math.sin(a) * (outRadius + 20);
      })
      .text(function(d, i) {
          return labels[i];
      })
      .style("text-anchor", function(d) {
        var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle + 10;
        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";
        }
      });

    svg.selectAll('path').transition().attrTween("d", arcTween);

And this is how I update the pie:

    let paths = svg.datum(values).selectAll('path').data(pie);
    let textLabels = svg.selectAll('.labelText').data(pie(values));

    paths.enter()
        .append('path')
        .attr('fill', function(d, i) { 
            if ( scale === 'linear') return color(d.value);
            return color(labels[i]); 
         })
         .attr('d', arc)
         .each(function(d) { this._current = d; });

    textLabels.enter()
        .append('text');

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

    textLabels.transition()
        .duration(1250)
      .attr("x", function(d) {
          var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
          d.cx = Math.cos(a) * (outRadius + 75);
          return d.x = Math.cos(a) * (outRadius + 30);
      })
      .attr("y", function(d) {
          var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
          d.cy = Math.sin(a) * (outRadius + 75);
          return d.y = Math.sin(a) * (outRadius + 20);
      })
      .style("text-anchor", function(d) {
        var rads = ((d.endAngle - d.startAngle) / 2) + d.startAngle + 10;
        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";
        }
       })
       .text(function(d, i) {
          return labels[i];
       });

  paths.exit().remove();
  textLabels.exit().remove();

  svg.transition().attr({width: width, height: height});

What am I doing wrong?

This pie-chart is an Ember component and its full code can be found here




Aucun commentaire:

Enregistrer un commentaire