score:2

Accepted answer

there are a few issues here

  1. your arc function is going to return random radii every call, which is not what i think you want. you could transition an arc from one inner/outer radius to the next, but for simplicity, let's say that each path only initially gets a random radius

  2. in order to transition from one the old pair of start/end angles to the new one you'll need to store the current angles somewhere. we'll store it in a local variable, which will be tied to each path

  3. because each path will have a different inner/outer radius, we'll need to have a different arc function for each segment as well.

working code here:

    var tau = 2 * math.pi; // http://tauday.com/tau-manifesto
    var overlap = 50;
    var currentsegment = d3.local();
    var segmentradius = d3.local();

    var jsonarcs = [
      { "base_radius": 370, "color" : "red"},
      { "base_radius": 330, "color" : "orange"},
      { "base_radius": 290, "color" : "yellow"},
      { "base_radius": 250, "color" : "green"},
      { "base_radius": 210, "color" : "blue" },
      { "base_radius": 170, "color" : "purple"},
      { "base_radius": 130, "color" : "black"},
      { "base_radius": 90, "color" : "red"}
    ];

    var arc = d3.arc()
        .innerradius(function() { return segmentradius.get(this).innerradius })
        .outerradius(function() { return segmentradius.get(this).outerradius });

    var center_def = d3.arc()
        .innerradius(0)
        .outerradius(60)
        .startangle(0);

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

    var path = g.selectall("path")
        .data(jsonarcs)
        .enter().append("path")
        .attr("fill", function(d, i) { return d.color; })
        .each(function(d) {
          var angles = randomangles();
          d.startangle = angles.startangle;
          d.endangle = angles.endangle;
          segmentradius.set(this, {
            innerradius: d.base_radius - overlap * math.random(),
            outerradius: d.base_radius + overlap * math.random()
          });
        })
        .attr("d", arc)
        .each(function(d) {currentsegment.set(this, d)});

    var center = g.append("path")
        .datum({endangle: tau})
        .style("fill", "black")
        .attr("d", center_def);

    d3.interval(function() {
      path.transition()
          .duration(750)
          .attrtween("d", arctween);
    }, 2500);

    function arctween() {
      var thispath = this;
      var interpolate = d3.interpolate(currentsegment.get(this), randomangles());
      currentsegment.set(this, interpolate(0));

      return function(t) {
        return arc.call(thispath, interpolate(t));
      };
    }
    function randomangles() {
      var angles = [math.random() * tau, math.random() * tau].sort();
      return {startangle: angles[0], endangle: angles[1]};
    }

notice a few things about the changed code:

  1. i set the random initial angles in an each call on the path just before the "d" attribute is set
  2. i stored the segment radii in the segmentradius d3.local variable at the end of that same chain and also after each interpolation is set in the call to the transition
  3. in the transition function i needed to call arc to preserve the 'this' of the path so that 'this' would be correct in arc.innerradius when retrieving the segmentradius.
  4. d3.interpolate can happily handle objects and not just numbers.

i have a similar example that i've been working on here if you'd like to learn more.


Related Query

More Query from same tag