Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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...

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...
  }
}

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"
        });
    });
});

Here is a module i put together a while back to assist in running animations sequentially.

Usage:

var seq = [
    { id: '#someelement', property:'opacity', initial: '0.0', value:'1.0', duration:500 },
    { id: '#somethingelse', property:'opacity', value:'1.0', duration: 500 }
];

Sequencer.runSequence(seq);

var Sequencer = (function($) {
    var _api = {},
        _seq = {},
        _seqCount = 0,
        _seqCallback = {};

    function doAnimation(count, step) {
        var data = _seq[count][step],
            props = {};

            props[data.property] = data.value

        $(data.id).animate(props, data.duration, function() {
            if (step+1 < _seq[count].length) {
                doAnimation(count, ++step);
            } else {
                if (typeof _seqCallback[count] === "function") {
                    _seqCallback[count]();
                }
            }
        });
    }

    _api.buildSequence = function(id, property, initial, steps) {
        var newSeq = [],
            step = {
                id: id,
                property: property,
                initial: initial
            };

        $.each(steps, function(idx, s) {
            step = {};
            if (idx == 0) {
                step.initial = initial;
            }
            step.id = id;
            step.property = property;
            step.value = s.value;
            step.duration = s.duration;
            newSeq.push(step);
        });

        return newSeq;
    }

    _api.initSequence = function (seq) {
        $.each(seq, function(idx, s) {              
            if (s.initial !== undefined) {
                var prop = {};
                prop[s.property] = s.initial;
                $(s.id).css(prop);
            }            
        });
    }

    _api.initSequences = function () {
        $.each(arguments, function(i, seq) {
            _api.initSequence(seq);
        });
    }

    _api.runSequence = function (seq, callback) {
        //if (typeof seq === "function") return;
        _seq[_seqCount] = [];
        _seqCallback[_seqCount] = callback;

        $.each(seq, function(idx, s) {

            _seq[_seqCount].push(s);
            if (s.initial !== undefined) {
                var prop = {};
                prop[s.property] = s.initial;
                $(s.id).css(prop);
            }

        });


        doAnimation(_seqCount, 0);
        _seqCount += 1;
    }

    _api.runSequences = function() {
        var i = 0.
            args = arguments,
            runNext = function() {
                if (i+1 < args.length) {
                    i++;
                    if (typeof args[i] === "function") {
                        args[i]();
                        runNext();
                    } else {
                        _api.runSequence(args[i], function() {
                            runNext();
                        });
                    }
                }
            };

        // first we need to set the initial values of all sequences that specify them
        $.each(arguments, function(idx, seq) {
            if (typeof seq !== "function") {
                $.each(seq, function(idx2, seq2) {
                    if (seq2.initial !== undefined) {
                        var prop = {};
                        prop[seq2.property] = seq2.initial;
                        $(seq2.id).css(prop);
                    }
                });
            }

        });

        _api.runSequence(arguments[i], function (){
            runNext();
        });

    }

    return _api;
}(jQuery));

jQuery can make synchronous animations. Check this out:

function DoAnimations(){
  $(function(){
    $("#myDiv").stop().animate({ width: 70 }, 500);
    $("#myDiv2").stop().animate({ width: 100 }, 500);
  });
}

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

EDIT The links seems dead. If I've followed the trail those the wayback archive correctly, the code is at https://github.com/feelinglucky/motion

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top