jquery animate callback not executing until final loop

196 Views Asked by At

I made a small test code using the jquery transit plugin for animations.

The purpose of the script is to have a photo of a tower make a flip 90 degrees toward the user, switch to another tower image, and flip another 90 degrees to lay flat on the screen. The issue is that after the first 90 degree flip, the image disappears entirely until the for loop has concluded before doing the final flip as the second image. I'm looking to have it flip continuously until the loop is finished.

I imagine this has something to do with closures and scope...

Javascript:

$(function() {
 for (var i = 0; i < 10; i++) {
  $('#test_flip')
   .css('background-image', 'url("tower1.jpg")')
   .transition({
    rotateY: '90deg'
   }, function() {
    $('#test_flip')
     .css('background-image', 'url("tower2.jpg")')
     .transition({
      rotateY: '180deg'
     });
   });
 };
});

jsfiddle: http://jsfiddle.net/ce9b9aja/

1

There are 1 best solutions below

0
On BEST ANSWER

The issue lies in the fact that the for loop calls .transition 10 times consecutively. The calls are queued in the jQuery queue (which is done behind the scenes by transit.js), but they are not queued in the order you're expecting.

Take the following example:

$(function () {
    $('#test').transition({x:40}, function () {
        $(this).transition({y:40});
    })
    
    $('#test').transition({scale:0.5}, function() {
        $(this).transition({skewX:"50deg"});
    });
});
#test {
    width: 10em;
    height: 10em;
    background-color: gray;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://ricostacruz.com/jquery.transit/jquery.transit.min.js"></script>
<div id="test"></div>

In this example, the first transition x:40 will be executed instantly because there is no queue. Despite the fact that it is executed instantly, since it is an animation, it will be using some form of setTimeout or setInterval and will not be completed in the transition method call. Consequently, the scale:0.5 transition will be called while the x:40 transition is still animating, which will put it in the queue it before y:40 is put in the queue.

Therefore, the queue order is

x:40 -> scale:0.5 -> y:40 -> skewX:50deg

Similarly, your code is producing the following queue:

rotateY:90deg -> ... -> rotateY:90deg -> rotateY:180deg -> ... -> rotateY:180deg

Thus your code's behavior. It rotates the image 90deg first, then it does it 9 more times, which doesn't change anything visually (hence the "pause"). Then it changes the image and rotates it 180deg and does that 9 more times also.

One solution could be to create a recursive function using the callback of the .transition function. An example is implemented below:

$(function() {
    FlipMe($('#test_flip'), "http://i.imgur.com/tYYtwbi.jpg", "http://i.imgur.com/G4CvJpc.jpg", 10)
});

function FlipMe($el, image1, image2, times) {
    $el.css('background-image', 'url("'+image1+'")')
        .transition({rotateY: '90deg'}, function() {
                $el.css('background-image', 'url("'+image2+'")')
                .transition({rotateY: '180deg'}, function() {
                    if(times > 0) {
                        FlipMe($el, image2, image1, times - 1);
                    }
                });
    });
}

Updated fiddle here: http://jsfiddle.net/ce9b9aja/1/

The code above exclusively uses callback functions to dictate the order of events. When the first transition is completed, the callback function will "queue" the next transition, which will be executed instantly since nothing else will be in the queue. And so on.