I'm creating a JS chart library and I'm struggling with pie/doughnut charts when I change the data. My visuals are created with SVG elements, and creating a new chart is fine. Where I'm struggling is changing the data on an existing chart. Once the chart has been drawn, I don't want the chart to "open up" when sectors are added or removed.
So for example, if my original data (say arrOld) is
arrOld = [
{name: "bacon", amount: 50},
{name: "eggs", amount: 25},
{name: "sausages", amount: 15},
{name: "toast", amount: 10}
];
and I want to animate a chart into a new data array (say, arrNew) which is
arrNew = [
{name: "bacon", amount: 50},
{name: "hash browns", amount: 8},
{name: "eggs", amount: 25},
{name: "toast", amount: 10},
{name: "sausages", amount: 15}
];
I want the new member "hash browns"
to "grow" from 0° between "bacon"
and "eggs"
, forcing them apart. That part I've pretty much implemented successfully. Because "toast"
and "sausages"
have swapped places, I want one of them animated (squashed) down to 0° (and then get removed) whilst a new replacement sector "grows" in its new position. So I will effectively have an intermediate array (say, arrTemp), which will look something like:
arrTemp = [
{name: "bacon", amount: 50},
{name: "hash browns", amount: 8, new: true},
{name: "eggs", amount: 25},
{name: "sausages", amount: 15, removeMe:true}
{name: "toast", amount: 10},
{name: "sausages", amount: 15, new:true}
];
The data I am actually dealing with is way more complex than this example, but this shows the basic idea. I can remove the temporary array after the animation is complete, and the removeMe
and new
properties are necessary for the animation, but don't need to remain after the animation has finished.
It is essential that the temporary array contains the members in the same order as the original array (even though they may have the "removeMe"
property set to true
) because that is the starting point of the transition animation.
I've written 5 or 6 for... loops and I'm really close to getting it working, but my code is horrible and I'm sure there's a better way of doing it. All I'm really doing is merging 2 arrays :o(
Any help gratefully received :o)
here's my current code:
var o;
//get the wedges from the previous animation call (the "old" array)
anWedges=gs.wedges[this._drawCall-1].clone();
//now find the new members:
//first set our new array to the current data:
var wedgesNew=gs.wedges[this._drawCall].clone();
//set "precedes" and "follows" properties:
for (w=0; w<wedgesNew.length; w++){
wedgesNew[w].precedes=null;
wedgesNew[w].follows=null;
if (w>0){
wedgesNew[w].follows=wedgesNew[w-1].id;
}
if(w<wedgesNew.length-1){
wedgesNew[w].precedes=wedgesNew[w+1].id;
}
}
//remove any members from our new array that were in the old one:
for (o=0; o<anWedges.length; o++){
anWedges[o].new=false;
anWedges[o].moved=false;
for (w=0; w<wedgesNew.length; w++){
if (anWedges[o].id===wedgesNew[w].id){
wedgesNew.splice(w,1);
break;
}
}
}
//add our new array members to the old array:
for (w=0; w<wedgesNew.length; w++){
wedgesNew[w].new=true;
//if its "follow" member doesn't exist, put it at the front:
if (wedgesNew[w].follows===null){
anWedges.unshift(wedgesNew[w]);
}
else{
//otherwise, put it where it should be:
for (o=0; o<anWedges.length; o++){
if (anWedges[o].id===wedgesNew[w].follows){
anWedges.splice(o+1, 0, wedgesNew[w]);
break;
}
}
}
}
//add a "follows" property to any members that don't have one:
for (o=0; o<anWedges.length; o++){
if (isNil(anWedges[o].follows) && o > 0){
for (w =0; w<gs.wedges[this._drawCall].length-1; w++){
if (gs.wedges[this._drawCall][w+1].id === anWedges[o].id){
anWedges[o].follows=gs.wedges[this._drawCall][w].id;
}
}
}
}
//see if we need to move any members:
var prevID="";
for (o=0; o<anWedges.length; o++){
var wRequired=false;
for (w=0; w<gs.wedges[this._drawCall].length;w++){
if (gs.wedges[this._drawCall][w].id===anWedges[o].id){
wRequired=true;
break;
}
}
if (!wRequired){
anWedges[o].removeMe=true;
}
else{
if (o>0 && !anWedges[o].new){
if(prevID!=="" && anWedges[o].follows!==prevID && !anWedges[o].removeMe){
var wMoved=cloneObj(anWedges[o]);
wMoved.moved=true;
anWedges[o].removeMe=true;
anWedges[o].id=anWedges[o].id+"_old";
for (w=0;w<anWedges.length;w++){
if (anWedges[w].id===anWedges[o].follows){
anWedges.splice(w+1,0, wMoved);
break;
}
}
}
}
prevID=anWedges[o].id;
}
}