Animating a series of lines in order with JQuery
I am having trouble figuring out the correct JQuery way of adding a series of animation to a chain. I understand that if you know your sequence ahead of time you can just to .animate().animate(), but each animation step is dynamic in my case. I think I need to change my traditional forloop into some type of Jquery .each() loop in order to chain everything so it will run in sequence.
I am working with Jquery.svg.anim.js (jquery svg animation).
I have an array of points with an associated draw time to the next step. "G" is a grid where my coordinate are G[x][y].x => P.x and G[x][y].y => P.x
step[0] = {P:G[0][0], T:500};
step[1] = {P:G[0][1], T:2000};
step[2] = {P:G[3][1], T:100};
I then run the above through this loop:
for(s=0;s<(step.length-1);s++){
var line1 = svg.line(step[s].P.x, step[s].P.y, step[s].P.x, step[s].P.y, {stroke: 'blue', strokeWidth: 10});
$(line1).animate({svgX2: step[s+1].P.x, svgY2: step[s+1].P.y}, step[s].T);
}
This works to draw all of the lines to the appropriate position and over the correct length of time, but of course because its not chained properly, its doing all the animation at once. The step[] array is going to be of an arbitrary length. If I need to change around data structures I can do that at this point.
How can I properly write this in a JQuery way where I can take in an array or some collection of steps and step through each animation consecutively? I eventually want to draw an arbitrary sequence of lines to points on the SVG grid in order and fade out each line after some delay. This is eventually going to be for a "Simon" beat/timing like game where the sequence grows larger and larger and you will have to retrace the sequence properly to the beat.
Edit: The following is working to load it in order, but it seems ugly because of two loops:
var line = new Array();
for(s=0;s<(step.length-1);s++){
line[s] = svg.line(step[s].P.x, step[s].P.y, step[s].P.x, step[s].P.y, {stroke: 'blue', strokeWidth: 10});
}
$.each(line, function(s, thisLine) {
$('#myQueue').queue('myQueue',function(next){
$(thisLine).animate(
{svgX2: step[s+1].P.x, svgY2: step[s+1].P.y}, //step[s].T
{duration: step[s].T, queue: false, complete: next}
)
})
});
$('#myQueue').dequeue('myQueue');
but, if I do it like this:
for(s=0;s<(step.length-1);s++){
line = svg.line(step[s].P.x, step[s].P.y, step[s].P.x, step[s].P.y, {stroke: 'blue', strokeWidth: 10});
n=s+1;
$('#myQueue').queue('myQueue',function(next){
$(line).animate(
{svgX2: step[n].P.x, svgY2: step[n].P.y},
{duration: step[s].T, queue: false, complete: next}
)
})
}
$('#myQueue').dequeue('myQueue');
It will only draw the last one; is this value vs reference? I tried wrapping the new line in an object but that did not seem to help. Update: From doing some testing the above does not work because the operations inside of the queue are no开发者_运维技巧t run until after they have been added, the "s" value has already been incremented all the way. That means the one with two loops is working because the iterator is restarted. I would wonder how to address this, hopefully without evals.
Final Version: The mysteries of scope in Javascript and Jquery seem to come into play. I have gotten this to work now and built the rest of my app's first iteration. I am still not really sure why certain placements and flows work and others don't, but for reference for anyone else who reads this:
$.each(step, function(s, thisStep) {
$('#myQueue').queue('myQueue',function(next){
var thisLine = svg.line(step[s].P.x, step[s].P.y, step[s].P.x, step[s].P.y, {stroke: lineColor()/*colours[random(9)]*/, strokeWidth: 10, id: 'line'+(s+1)});
$(thisLine).animate(
{svgX2: step[s+1].P.x, svgY2: step[s+1].P.y}, //step[s].T
{duration: step[s].T, queue: true, complete: next}
);
});
});
$('#myQueue').dequeue('myQueue');
Since I find this confusing myself, let me try to explain this for others who float by: the $.each allows you to do an iteration loop over an array. There is no for counters, so you need to add in a sentinel check at the top of the nest or do other logic if you don't want to iterate through everything (I want to iterate only based on a specific limit so I used the following "if(!(s < Limit)){ return; }". For each iteration, we are going to add to a named queue, as opposed to the default "fx" queue. This allows us to group a bunch of steps in the animation then run them all in once, in order (not at the same time) when you call "dequeue". What's important to note is the callback function named "next", which is the second parameter for the queue function - why? Because in the collection which is the second parameter of "animate()" we have this "complete: next" This means call the function next when you are done, the callback. The other important setting there is "queue: true", if set to false everything will happen at the same time. I am not sure if this sets up recursion per-se or just adds to the queue in order, but this setup works for me. Note, you do not have to have the queue call inside a loop. If you are wondering about the div#myQueue, its just a hidden div in the DOM to hold the named queue, and could probably be something that already exists and has a real purpose.
I hope that explanation helps someone or that someone more skilled in JFoo will add some comments below.
You can just use delay:
var totalDelay=0;
for(s=0;s<(step.length-1);s++){
var line1 = svg.line(step[s].P.x, step[s].P.y, step[s].P.x, step[s].P.y, {stroke: 'blue', strokeWidth: 10});
$(line1).delay(totalDelay).animate({svgX2: step[s+1].P.x, svgY2: step[s+1].P.y}, step[s].T);
totalDelay+=step[s].T;
}
You could also consider using your own jquery queue. Something similar to this: Single queue for jQuery animate() elements
A similar question was just asked, you may find the answer there:
Animate elements sequentially using CSS3 and jQuery
I was also just messing with a fiddle for my own curiosity, but the question above might have a better solution than my fiddle:
http://jsfiddle.net/jensbits/XjEjx/
精彩评论