Update pie slice size in Raphael pie chart

2020-07-18 08:29发布

问题:

I'm working on making a pie chart that shows results over time. As such it needs to animate between states to show how different slices change. I've figured out how to change all the slices en masse (using this example as a starting point), but I would like to be able to select and manage a particular slice (or sector as Raphael calls it) at a time. Has anyone figured out how to do that? I've found that if var pie is my pie chart, then I can get a particular slice with:

var pie = r.g.piechart(200, 200, 150, dataArray);
slice = pie.series[0];

But when I attempt to change the slice with say, an animation (specifically to change its size), that fails (segment is not the right way?):

slice.animate({segment: [200, 200, 0, 100]}, 800);

Any insight into manipulating individual slices would be very helpful.

回答1:

I realized with much embarrassment that the segment attribute was a custom attribute created and used in an example I found to update the path of a pie slice, and therefore its size. It looks like this:

  var r = Raphael("holder");
  r.customAttributes.segment = function (x, y, r, a1, a2) {
      var flag = (a2 - a1) > 180,
          clr = (a2 - a1) / 360;
      a1 = (a1 % 360) * Math.PI / 180;
      a2 = (a2 % 360) * Math.PI / 180;
      return {
          path: [["M", x, y], ["l", r * Math.cos(a1), r * Math.sin(a1)], ["A", r, r, 0, +flag, 1, x + r * Math.cos(a2), y + r * Math.sin(a2)], ["z"]],
          fill: "hsb(" + clr + ", .75, .8)"
      };
  };

Here's what this might look like in context: I have three values [10, 20, 15] that total to 45. Assuming a circle with a width and height of 250, I can populate the circle with slices using the segment custom attribute like this (assuming I have a div on my page with the id of holder):

var r = Raphael("holder");
r.customAttributes.segment = function (x, y, r, a1, a2) {
    var flag = (a2 - a1) > 180,
        clr = (a2 - a1) / 360;
    a1 = (a1 % 360) * Math.PI / 180;
    a2 = (a2 % 360) * Math.PI / 180;
    return {
        path: [["M", x, y], ["l", r * Math.cos(a1), r * Math.sin(a1)], ["A", r, r, 0, +flag, 1, x + r * Math.cos(a2), y + r * Math.sin(a2)], ["z"]],
        fill: "hsb(" + clr + ", .75, .8)"
    };
};
points = [10, 20, 15];
total = 45;
start = 0;
paths = [];
for(i=0; i<=2; i++) {
  size = 360 / total * points[i];
  var slice = r.path();
  slice.attr({segment: [250, 250, 200, start, start + size], stroke: "#000", title: "Slice "+i});
  paths.push(slice);
  start += size;
}

I can then animate the slices in my paths array whenever I want, by animating the segment attribute:

newPoints = [5, 20, 20];
start = 0;
for(i=0; i<=2; i++) {
  size = 360 / total * newPoints[i];
  paths[i].animate({segment: [250, 250, 200, start, start + size]}, 800);
  paths[i].angle = start - size / 2;
  start += size;
}

Some of it I understand, some of it I don't. But the above code will work (I checked).