How can I animate multiple elements sequentially u

2019-01-04 09:46发布

I thought it would be simple but I still can't get it to work. By clicking one button, I want several animations to happen - one after the other - but now all the animations are happening at once. Here's my code - can someone please tell me where I'm going wrong?:

$(".button").click(function(){
  $("#header").animate({top: "-50"}, "slow")
  $("#something").animate({height: "hide"}, "slow")
  $("ul#menu").animate({top: "20", left: "0"}, "slow")
  $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
});

9条回答
你好瞎i
2楼-- · 2019-01-04 10:13

A slight improvement on @schmunk's answer is to use a plain object jQuery object's queue in order to avoid conflicting with other unrelated animations:

$({})
    .queue(function (next) {
        elm1.fadeOut('fast', next);
    })
    .queue(function (next) {
        elm2.fadeIn('fast', next);
    })
    // ...

One thing to keep in mind is that, although I have never run into problems doing this, according to the docs using the queue methods on a plain object wrapper is not officially supported.

Working With Plain Objects

At present, the only operations supported on plain JavaScript objects wrapped in jQuery are: .data(),.prop(),.bind(), .unbind(), .trigger() and .triggerHandler().

查看更多
劫难
3楼-- · 2019-01-04 10:14

Animate Multiple Tags Sequentially

You can leverage jQuery's built-in animation queueing, if you just select a tag like body to do global queueing:

// Convenience object to ease global animation queueing
$.globalQueue = {
    queue: function(anim) {
        $('body')
        .queue(function(dequeue) {
            anim()
            .queue(function(innerDequeue) {
                dequeue();
                innerDequeue();
            });
        });

        return this;
    }
};

// Animation that coordinates multiple tags
$(".button").click(function() {
    $.globalQueue
    .queue(function() {
        return $("#header").animate({top: "-50"}, "slow");
    }).queue(function() {
      return $("#something").animate({height: "hide"}, "slow");
    }).queue(function() {
        return $("ul#menu").animate({top: "20", left: "0"}, "slow");
    }).queue(function() {
        return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");
    });
});

http://jsfiddle.net/b9chris/wjpL31o0/

So, here's why this works, and what it's doing:

  1. The call to $.globalQueue.queue() is just queueing a call to your tag's animation, but it queues it on the body tag.

  2. When jQuery hits your tag animation in the body queue, your tag's animation starts, on the queue for your tag - but the way the jQuery animation framework works, any custom animation callback causes a tag's animation queue (the body's in this case) to halt, until the custom animation calls the passed-in dequeue() function. So, even though the queues for your animated tag and body are separate, the body tag's queue is now waiting for its dequeue() to be called. http://api.jquery.com/queue/#queue-queueName-callback

  3. We just make the last queued item on the tag's queue a call to continue the global queue by calling its dequeue() function - that's what ties the queues together.

  4. For convenience the globalQueue.queue method returns a this reference for easy chaining.

setInterval

For the sake of completeness, it's easy to land here just seeking an alternative to setInterval - that is you're not so much looking to make separate animations coordinate, as just fire them over time without the strange surge ahead in your animation caused by the way newer browsers will postpone animation queues and timers to save CPU.

You can replace a call to setInterval like this:

setInterval(doAthing, 8000);

With this:

/**
 * Alternative to window.setInterval(), that plays nicely with modern animation and CPU suspends
 */
$.setInterval = function (fn, interval) {
    var body = $('body');
    var queueInterval = function () {
        body
        .delay(interval)
        .queue(function(dequeue) {
            fn();
            queueInterval();
            dequeue();  // Required for the jQuery animation queue to work (tells it to continue animating)
        });
    };
    queueInterval();
};

$.setInterval(doAthing, 8000);

http://jsfiddle.net/b9chris/h156wgg6/

And avoid those awkward blasts of animation when a background tab has its animations re-enabled by the browser.

查看更多
The star\"
4楼-- · 2019-01-04 10:15

You can also put your effects into the same queue, i.e. the queue of the BODY element.

$('.images IMG').ready(
   function(){
        $('BODY').queue(
            function(){
                $('.images').fadeTo('normal',1,function(){$('BODY').dequeue()});
            }
        );
    }
);

Make sure you call dequeue() within the last effect callback.

查看更多
孤傲高冷的网名
5楼-- · 2019-01-04 10:18

This has already been answered well (I think jammus's answer is the best) but I thought I'd provide another option based on how I do this on my website, using the delay() function...

  $(".button").click(function(){
     $("#header").animate({top: "-50"}, 1000)
     $("#something").delay(1000).animate({height: "hide"}, 1000)
     $("ul#menu").delay(2000).animate({top: "20", left: "0"}, 1000)
     $(".trigger").delay(3000).animate({height: "show", top: "110", left: "0"}, "slow");
});

(replace 1000 with your desired animation speed. the idea is your delay function delays by that amount and accumulates the delay in each element's animation, so if your animations were each 500 miliseconds your delay values would be 500, 1000, 1500)

edit: FYI jquery's 'slow' speed is also 600miliseconds. so if you wanted to use 'slow' still in your animations just use these values in each subsequent call to the delay function - 600, 1200, 1800

查看更多
Deceive 欺骗
6楼-- · 2019-01-04 10:24

I know this is an old question, but it should be updated with an answer for newer jQuery versions (1.5 and up):

Using the $.when function you can write this helper:

function queue(start) {
    var rest = [].splice.call(arguments, 1),
        promise = $.Deferred();

    if (start) {
        $.when(start()).then(function () {
            queue.apply(window, rest);
        });
    } else {
        promise.resolve();
    }
    return promise;
}

Then you can call it like this:

queue(function () {
    return $("#header").animate({top: "-50"}, "slow");
}, function () {
    return $("#something").animate({height: "hide"}, "slow");
}, function () {
    return $("ul#menu").animate({top: "20", left: "0"}, "slow");
}, function () {
    return $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");        
});
查看更多
叛逆
7楼-- · 2019-01-04 10:26

You could do a bunch of callbacks.

$(".button").click(function(){
    $("#header").animate({top: "-50"}, "slow", function() {
        $("#something").animate({height: "hide"}, "slow", function() {
            $("ul#menu").animate({top: "20", left: "0"}, "slow", function() {
                $(".trigger").animate({height: "show", top: "110", left: "0"}, "slow");        
            });
        });
    });
});
查看更多
登录 后发表回答