I have ranking data for the top n employees that I'd like to use to drive a visualization. I know that because the number of employees, n, that are displayed will never change, if I update the data join using a composite (employee + rank) key function, the enter and exit selections should always contain the same number of elements and I should receive an exiting/entering employee pair everytime an employee's rank changes.
Now I'm trying to animate the exiting and entering of elements. Using the technique described by Mr. Bostock in d3: How to properly chain transitions on different selections I can chain two transitions and apply the first to the exiting selection and the second to the entering selection to sequence transitions on different selections.
The problem comes when I try to embellish the exit transition with sub-transitions (e.g. turning the exiting transition into a two-part, chained transition composed of a text tween and then background transition). As the enter transition doesn't know about the number of sub-transitions (nor the delay/duration) in the exit transition, I have to manually compute the enter transition delay/duration. Furthermore, I either have to calculate from the data how long the delay/duration of the sub-transitions are or have to assume the longest delay/duration possible for every transition on every element in the exiting selection (e.g. I have to assume the sub-transition to tween the text for every employee is tweening the longest possible employee name).
Is there some way, since I have the same number of exiting and entering elements, to chain transitions like they were on the same elements? I.e. with the same selection I can chain transitions such that subsequent transitions inherit delay/duration, can I do this with disparate selections of the same size? Basically, how can I write the following without having to estimate delay/duration?
function chart() {
var scale = d3.scale.ordinal().rangeBands([0, 150], .5);
return function(selection) {
selection.each(function(data) {
scale.domain(data);
var svg = d3.select(this).selectAll('svg').data([data]);
var svgEnter = svg.enter().append('svg');
var g = svg.selectAll('g').data(function(d) {
return d;
}, function(d, i) {
return d + i;
}).attr('class', 'update');
var gExit = g.exit();
var gEnter = g.enter().append('g').attr('class', 'enter').attr('transform', function(d) {
return 'translate(150 ' + scale(d) + ')';
});
g.order();
var gExitTransition = d3.transition().each(function() {
var t0 = gExit.transition().delay(function(_, i) {
return i * 100;
}).duration(function(d) {
return d.length * 250 / 5;
});
var t1 = t0.transition();
var t2 = t1.transition();
t0.select('text').tween('text', function(d) {
var i = d3.interpolate(this.textContent.length, 0);
return function(t) {
this.textContent = d.slice(0, i(t));
};
});
t1.select('rect').attr('y', scale.rangeBand() / 2).attr('height', 1e-6);
t2.remove();
});
var gEnterTransition = gExitTransition.transition().each(function() {
var t0 = gEnter.transition().delay(function(_, i) {
return i * 100 + 500;
});
var t1 = t0.transition().duration(function(d) {
return d.length * 250 / 5;
});
var rectEnter = gEnter.append('rect').attr('y', scale.rangeBand() / 2).attr('width', 50).attr('height', 1e-6).style('fill', 'rgba(0, 0, 0, .1)');
t0.select('rect').attr('y', 0).attr('height', scale.rangeBand());
var textEnter = gEnter.append('text').attr('y', scale.rangeBand() / 2).style('alignment-baseline', 'middle');
t1.select('text').tween('text', function(d) {
var i = d3.interpolate(this.textContent.length, d.length);
return function(t) {
this.textContent = d.slice(0, i(t));
};
});
});
});
};
}
var alphabet = 'abcdefghijklmnopqrstuvwxyz'.split('').map(function(c) {
return d3.range(Math.floor(5 * Math.random()) + 1).map(function() {
return c;
}).join('');
});
var myChart = chart();
var selection = d3.select('body');
(function(f) {
f();
setInterval(f, 3000);
})(function() {
selection.datum(d3.shuffle(alphabet.slice(0)).slice(0, 10).sort()).call(myChart);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>