问题
I have a sequential data. Data is an array divided into 30 steps. In the first step I draw initial data. Then I take data from second step, compare it with data from previous step. And make some animation/transition.
Now I want to repeat animation for the rest of 28 steps. If I will use code like this
var old_data = start_data;
for (var i = 0, i < rest_data.length; i++){
var new_data = rest_data[i];
var diff_data = diff(old_data, new_data);
var t0 = d3.transition().duration(3000);
var selection = d3.selectAll('g').data(diff_data)
selection.transition(t0).attr('my', function(d,i) { ... });
old_data = new_data;
}
it will not work because transition() is async code.The loop run faster than complete the first animation. And at best I will have an animation between data[0] and data[29].
What is the best way to create an animation with sequential data like my?
Use d3.transition().on('end', function() {...}), where function() { ... } some function with closure where I will store old_data, rest_data and other variables?
I don't like this solution because I have many variables to calculate diff_data, and I have to keep them in the closure.
回答1:
You are correct: the for loop will run to the end almost instantly, and it will simply call that bunch of transitions at the same time, which, of course,
will not work.
There are some different solutions here. Good news is that transition().on("end"... is just one of them, you don't need to use it if you don't want.
My favourite one is creating a function that you call repeatedly with setTimeout or, even better, with d3.timeout.
This is the logic of it:
var counter = 0;//this is, of course, a counter
function loop(){
//if there is a data element for the next counter index, call 'loop' again:
if(data[counter + 1]){ d3.timeout(loop, delay)}
//access the data using the counter here,
//and manipulate the selection
//increase the counter
counter++;
}
Have a look at this demo:
var data = [30, 400, 170, 280, 460, 40, 250];
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 100);
var circle = svg.append("circle")
.attr("cy", 50)
.attr("cx", 250)
.attr("r", 10)
.attr("fill", "tan")
.attr("stroke", "black");
var counter = 0;
loop();
function loop() {
if (data[counter + 1]) {
d3.timeout(loop, 1000)
}
circle.transition()
.duration(500)
.attr("cx", data[counter]);
counter++
}
<script src="https://d3js.org/d3.v4.min.js"></script>
来源:https://stackoverflow.com/questions/45258853/how-to-do-animation-of-sequential-data-with-d3-js