views:

1541

answers:

7

In many cases I wish animation to be executed synchronously. Especially when I wish to make a a series of sequential animations.

Is there an easy way to make a jQuery animate function call synchronous?

The only way I thought about is to set a flag true when the animation has finished and to wait for this flag.

+5  A: 

jQuery cannot make synchronous animations.

Remember that JavaScript runs on the browser's UI thread.

If you make a synchronous animation, the browser will freeze until the animation finishes.

Why do you need to do this?

You should probably use jQuery's callback parameter and continue your method code in the callback, like this:

function doSomething() {
    var thingy = whatever;
    //Do things
    $('something').animate({ width: 70 }, function() {
        //jQuery will call this method after the animation finishes.
        //You can continue your code here.
        //You can even access variables from the outer function
        thingy = thingy.fiddle;
    });
}

This is called a closure.

SLaks
I think you can escape from UI thread with setTimeout, so that I can definitely use nonblocking animation in a setTimeout function to have a sane looking code for sequential animations.
Elazar Leibovich
You are wrong. `setTimeout` does not execute the callback on a different thread; it waits for the UI thread to become free and then invokes the callback on the UI thread. Therefore, Javascript developers don't need to deal with all of the intricacies of thread-safe development.
SLaks
test my answer solution please
CuSS
+1  A: 

I agree with @SLaks on this one. You should be using jQuery's callbacks for given animations to create your synchronous animation. You can essentially take whatever you have for your current animation and split it up like so:

$yourClass = $('.yourClass');
$yourClass.animate({
    width: "70%"
}, 'slow', null, function() {
    $yourClass.animate({
        opacity: 0.4
    }, 'slow', null, function() {
        $yourClass.animate({
            borderWidth: "10px"
        });
    });
});
cballou
You probably understand how it would look when it scales to 20 actions... also see my response to @SLaks.
Elazar Leibovich
If you don't like the way it looks, try not indenting the callbacks (or only indenting them a little bit). This is the only way to do it.
SLaks
A: 

jQuery provides a "step" callback for its .animate() method. You can hook into this to do synchronous animations:

jQuery('#blat').animate({
  // CSS to change
  height: '0px'
},
{
  duration: 2000,
  step: function _stepCallback(now,opts) {
    // Stop browser rounding errors for bounding DOM values (width, height, margin, etc.)
    now = opts.now = Math.round(now);

    // Manipulate the width/height of other elements as 'blat' is animated
    jQuery('#foo').css({height: now+'px'});
    jQuery('#bar').css({width: now+'px'});
  },
  complete: function _completeCallback() {
    // Do some other animations when finished...
  }
}
brownstone
The step callback has nothing to do with the question. The complete callback is exactly what the other answers are saying.
SLaks
He is trying to execute one animation after another one finishes. He is not trying to animate two elements at once.
SLaks
My apologies -- my brain inserted an "a" where there wasn't one!
brownstone
A: 

I think you should take a look at the jQuery queue() method.

Not only does queue()'s doc explain jQuery animations don't really block the UI, and actually queues them after one another.

It also provides with a way to make your animations and function calls sequential (this is my best understanding of what you mean by "synchronous"), like:

$("#myThrobber")
    .show("slow")                 // provide user feedback 
    .queue( myNotAnimatedMethod ) // do some heavy duty processing
    .hide("slow");                // provide user feedback (job's 

myNotAnimatedMethod() { // or animated, just whatever you want anyhow...
    // do stuff
    // ...

    // tells #myThrobber's ("this") queue your method "returns", 
    // and the next method in the queue (the "hide" animation) can be processed
    $(this).dequeue();

    // do more stuff here that needs not be sequentially done *before* hide()
    // 
}  

This is of course overkill with asynchronous processing; but if your method is actually a plain old synchronous javascript method, that could be the way to do it.

Hope this helps, and sorry for my poor english...

Alain Saint-Etienne
synchronous means `fwrite(big_data)` returns AFTER `fwrite` finished writing. Asynchronous means `fwrite` would return immediately, and writing the big data is done in parallel/some other time.
Elazar Leibovich
Then I think I got it right: if you whish you animation of $("#myThrobber") for instance to be executed synchronously after fwrite(big_data), you can do so with the following two statements: (1) $("#myThrobber").queue(fwrite(big_data)).animate(//whatever); AND (2) end you fwrite() method with $("#myThrobber").dequeue();NB: my sample was wrong on calling $(this).dequeue(); it should read:$("#myThrobber").dequeue();Forgot that as I created that sample from code where I needed to .queue( jQuery.proxy(myNotAnimatedMethod,this) )Sorry for that confusion.
Alain Saint-Etienne
A: 

I came across this http://lab.gracecode.com/motion/ Really easy to use and works great in combination with jquery.

John van Dijk
@John, I wish it had English documentation. Thanks anyhow.
Elazar Leibovich
A: 

In some case you can truly need this .animate() function to be synchronous. It would be nice if anyone find a way of doing this.

Danny Coulombe
A: 

Hey, i think this is very interesting...

i've tryed to construct some code, can someone give it a try or tell me how to improve it?

thanks to all...

i=0;
var animatelist = function(animationlist){
    if(i==0) var animations = animationlist;
    var timer = 0;
    i++;
    if( (animations[i]['selector']) && (animations[i]['properties']) ){
        if(!animations[i]['time']) animations[i]['time'] = 500;
        $(animations[i]['selector']).animate(animations[i]['properties'],animations[i]['time'],animatelist);
    }
}


//Example

animatelist({
    1: {
        'selector': "#mydiv",
        'properties': {...},
        'time': 500
    },
    2: {
        'selector': "#mydiv",
        'properties': {...},
        'time': 500
    },
    3: {
        'selector': "#mydiv",
        'properties': {...},
        'time': 500
    }
});
CuSS
You should ask a separate question.
SLaks
Also, you should take an array.
SLaks