user4534236
user4534236

Reputation:

d3 pie sort transition

How would I apply an animated transition when sorting a pie chart? I don't mean updating values, I mean sorting each arc of the pie to move into place the way bars do here.

There are plenty of transition examples for updating using new values of an existing data set (or switching data sets). I can't find anything on how to re-sort (using the same values, same data set).

I'm using this for now which simply redraws the arc by applying the same tween used when initializing the rendering, but it starts each arc from zero.

        .attr('d', arc)
        .transition()
        .duration(1000)
        .attrTween("d", tweenPie);

    function tweenPie(b) {
        var i = d3.interpolate({startAngle: 0, endAngle: 0}, b);
        return function(t) { return arc(i(t)); };
    };

Would I need to store the existing start and end angles somehow and run the tween from those?

I see something kind of like that here, though this example is updating values, not sorting.

Thanks.

Upvotes: 0

Views: 1324

Answers (1)

Mark
Mark

Reputation: 108512

Building on the Bostock example here.

  setInterval(change, 2000);

  var sort = false;
  function change() {

    sort = !sort;

    if (sort){
      pie = d3.layout.pie() //<-- pie with default sort
        .value(function(d) {
          return d.value;
        });
    } else {
      pie = d3.layout.pie() //<-- pie with no sort
        .value(function(d) {
          return d.value;
        })
        .sort(null);
    }

    path = path.data(pie); // compute the new angles
    path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
  }

Full code:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  body {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin: auto;
    position: relative;
    width: 400px;
  }
  
  text {
    font: 10px sans-serif;
  }
  
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<body>
<script>
  var width = 400,
    height = 500,
    radius = Math.min(width, height) / 2;
    
  var color = d3.scale.category20();
  
  var pie = d3.layout.pie()
    .value(function(d) {
      return d.value;
    })
    .sort(null);
    
  var defaultSort = pie.sort;
    
  var arc = d3.svg.arc()
    .innerRadius(radius - 100)
    .outerRadius(radius - 20);
    
  var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

  var data = [{
      value: 1
    }, {
      value: 5
    }, {
      value: 2
    }, {
      value: 6
    }
  ];

  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
    
  setInterval(change, 2000);

  var sort = false;
  function change() {

    sort = !sort;

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

    path = path.data(pie); // compute the new angles
    path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
  }

  // 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));
    };
  }
</script>
</body>

Upvotes: 1

Related Questions