3

I'm using Snap.svg to animate 13 SVGs. All the SVGs start from one point and animate through several other animations (through callbacks). An simplified example of my animation is described, in code, below:

var reset = function(person) {
    person.attr({transform: 't0,0', opacity: 0});
    animatePerson(person);
};

var animatePerson = function(person) {
    person.animate({opacity: 1}, 300, null,function() {
        person.animate({transform: 't100,20'}, 1000, null, function() {
            person.animate({opacity: 0}, 300, null, function() {
                reset(person);
            });
        });
    });
};

var people = [Snap('#Person_1'), Snap('#Person_2'), Snap('#Person_3'), Snap('#Person_4'), Snap('#Person_5')];

My first attempt was to map over the array and set a timeout for the first animation like so:

people.map(function(person, index) {
    setTimeout(function() {
        animatePerson(person);
    }, (300*index));
});

However this did not work as the SVGs would start to overlap/overtake each other when they looped. Then I tried to set the timeout equal to the length of time it took for one complete "lap" of the animation and divide that by the total amount of SVGs like so:

people.map(function(person, index) {
    setTimeout(function() {
        animatePerson(person);
    }, (1600/people.length));
});

Is there a way in Snap.svg or JavaScript to have the animation loop using callbacks and/or timeouts, or an I way off here?

Here is an image of the full animation I am referring to:

enter image description here

Enijar
  • 6,387
  • 9
  • 44
  • 73
  • 2
    Unrelated note: `Array.prototype.map` is for creating a new array by manipulating the existing elements in an array. If you just want to iterate over an array, use `Array.prototype.forEach` to make your intentions clearer (and avoid creating the extra array that map generates) – Ruan Mendes Jun 29 '15 at 18:25

1 Answers1

2

One way I have done this, is to write a small function that takes in an element and an array of preset animations, and go through them in turn via the callback, which looks like this...

Edit: Modified slightly to be able to include a different element per animation, and example2 link below, includes the option to include a function to call each time as well.

function nextFrame ( frameArray,  whichFrame ) {
    if( whichFrame >= frameArray.length ) { return }
    frameArray[ whichFrame ].el.animate(    
          frameArray[ whichFrame ].animation,
          frameArray[ whichFrame ].dur,
          frameArray[ whichFrame ].easing,
          nextFrame.bind(null,  frameArray, whichFrame + 1 )
    );
}

Then you could pass it an array of anims, like this...

var myFrames = [
    {   el: g,  animation: { transform: 'r360,150,150' }, dur: 1000, easing: mina.bounce },
    {   el: r,  animation: { transform: 't100,-100s2,3' }, dur: 1000, easing: mina.bounce },
    {   el: r,  animation: { transform: 't100,100' }, dur: 1000, easing: mina.bounce },
    {   el: g,  animation: { transform: 's2,1' }, dur: 1000, easing: mina.bounce },
    {   el: r,  animation: { transform: 's1,2' }, dur: 1000, easing: mina.bounce },
    {   el: c,  animation: { transform: 's1,1' }, dur: 1000, easing: mina.bounce }];

Then you can call it with

nextFrame( el, myFrames, 0 );

example (click run on there)

example 2 (this allows you to include a func to call as well as part of it)

Ian
  • 13,724
  • 4
  • 52
  • 75
  • This works well for animating multiple animations on one element, but not multiple animations on multiple elements. The issue I have is with elements overlapping each other. – Enijar Jun 30 '15 at 10:17
  • Easy enough to just add the element in there. I've edited the post and link to an example showing it with an array that includes the element. – Ian Jun 30 '15 at 10:32