I have a sunburst chart made in D3. Each 'petal' represents a subset of data. When a user clicks on one of the 'petals', I would like it to transition, fanning out to only show that subset (see image):
I'm having trouble getting the code to properly transition.
On click, all 'petals' (besides the selected one) should disappear and the remain paths should animate along the circle (using attrTween, arcTween, and interpolate?). The primary value that would be changing is the angleSize (var angleSize = (2 * Math.PI) / theData.length;
).
I've tried using this, this, this, and this as reference without much success. What's the best way to handle the animation?
Thanks for your time!
--> See Plunker Here. <--
Code is below:
var colors = {
'Rank1' : '#3FA548',
'Rank2' : '#00B09E',
'Rank3' : '#8971B3',
'Rank4' : '#DFC423',
'Rank5' : '#E74341'
};
var $container = $('.chart'),
m = 40,
width = $container.width() - m,
height = $container.height() - m,
r = Math.min(width, height) / 2;
var study = null;
var arc = d3.svg.arc();
d3.csv('text.csv', ready);
function ready(err, data) {
if (err) console.warn('Error', err);
var svg = d3.select('.chart')
.append('svg')
.attr({
'width' : (r + m) * 2,
'height' : (r + m) * 2,
'class' : 'container'
})
.append('g')
.attr('transform', 'translate(' + (width / 4) + ', ' + (height / 2) + ' )');
var slice = svg.selectAll('.slice');
function updateChart(study) {
if (study) {
var theData = data.filter(function(d) {
return d.study_name === study;
});
} else {
var theData = data;
}
slice = slice.data(theData);
slice.enter()
.append('g')
.attr('class', 'slice');
var angleSize = (2 * Math.PI) / theData.length;
var startRadArr = [],
endRadArr = [];
for ( var i = 0; i < data.length; i++ ) {
var startRadius = (width / 20),
endRadius = startRadius;
for ( var x = 0; x < 4; x++ ) {
startRadArr.push(startRadius);
if ( x == 0 ) {
endRadius += Number(data[i].group1_score) * (width / 500);
} else if ( x == 1 ) {
endRadius += Number(data[i].group2_score) * (width / 500);
} else if ( x == 2 ) {
endRadius += Number(data[i].group3_score) * (width / 500);
} else {
endRadius += Number(data[i].group4_score) * (width / 500);
}
endRadArr.push(endRadius);
startRadius = endRadius + 0.3;
}
}
var startRadGroup = [],
endRadGroup = [];
for (i = 0; i < startRadArr.length; i += 4) {
startRadGroup.push(startRadArr.slice(i, i + 4));
}
for (i = 0; i < endRadArr.length; i += 4) {
endRadGroup.push(endRadArr.slice(i, i + 4));
}
slice.selectAll('path')
.remove();
for ( var x = 0; x < 4; x++ ) {
slice.append('path')
.attr({
'class' : function(d, i) {
if ( x == 0 ) {
return d.group1_class;
} else if ( x == 1 ) {
return d.group2_class;
} else if ( x == 2 ) {
return d.group3_class;
} else {
return d.group4_class;
}
},
'company' : function(d, i) {
return d.brand_name;
},
'cat' : function(d, i) {
if ( x == 0 ) {
return 'Group1';
} else if ( x == 1 ) {
return 'Group2';
} else if ( x == 2 ) {
return 'Group3';
} else {
return 'Group4';
}
},
'study' : function(d, i) {
return d.study_name;
},
'companyid' : function(d, i) {
return d.brand_id;
},
'startradius' : function(d, i) {
return startRadGroup[i][x];
},
'endradius' : function(d, i) {
return endRadGroup[i][x];
},
'startangle' : function(d, i) {
return angleSize * i;
},
'endangle' : function(d, i) {
return angleSize * (i + 1);
}
})
.on('click', selectStudy);
}
slice.exit()
.remove();
slice.selectAll('path')
.attr({
'd' : function(d) {
return arc({
innerRadius : +d3.select(this)[0][0].attributes.startradius.nodeValue,
outerRadius : +d3.select(this)[0][0].attributes.endradius.nodeValue,
startAngle : +d3.select(this)[0][0].attributes.startangle.nodeValue,
endAngle : +d3.select(this)[0][0].attributes.endangle.nodeValue
})
}
});
}
function selectStudy(d) {
study = $(this).attr('study');
updateChart(study);
}
updateChart();
}
EDIT
Updated the code (based on this) to include a properly working enter, update, and exit pattern. Still unsure about the transition however. Most of the examples I've linked to use something similar to d3.interpolate(this._current, a);
, tweening between differing data.
In this chart, this._current and a are the same, angleSize (var angleSize = (2 * Math.PI) / theData.length;
), startAngle, and endAngle are the only thing that changes.