如何使用jQuery按顺序为多个元素设置动画?

时间:2009-08-02 04:28:27

标签: jquery animation queue

我认为这很简单,但我仍然无法让它发挥作用。通过单击一个按钮,我想要发生几个动画 - 一个接一个 - 但现在所有的动画都会立即发生。这是我的代码 - 有人可以告诉我哪里出错了吗?:

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

10 个答案:

答案 0 :(得分:28)

队列仅在您动画相同元素时才有效。上帝知道上面为什么投了票,但是不行。

您需要使用动画回调。您可以将函数作为最后一个参数传递给animate函数,并在动画完成后调用它。但是,如果您有多个带回调的嵌套动画,则脚本将变得非常难以理解。

我建议使用following插件重写原生jQuery动画功能,并允许您指定队列名称。您使用相同队列名称添加的所有动画将按演示here顺序运行。

示例脚本

  $("#1").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
  $("#2").animate({marginTop: "100px"}, {duration: 100, queue: "global"});
  $("#3").animate({marginTop: "100px"}, {duration: 100, queue: "global"});

答案 1 :(得分:25)

我知道这是一个老问题,但它应该更新为新的jQuery版本(1.5及更高版本)的答案:

使用$.when功能,您可以编写此帮助程序:

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

然后你可以这样称呼它:

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

答案 2 :(得分:24)

你可以做一堆回调。

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

答案 3 :(得分:6)

@ schmunk的答案略有改进是使用普通对象jQuery对象的队列,以避免与其他无关的动画冲突:

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

要记住的一件事是,虽然我从来没有遇到过这样的问题,但根据the docs使用普通对象包装器上的队列方法并不是官方支持的。

  

使用普通对象

     

目前,jQuery包含的纯JavaScript对象支持的唯一操作   是:.data(),. prop(),. bind(),. unbind(),. strigger()和.triggerHandler()。

答案 4 :(得分:2)

延伸到jammus的回答,这对于长动画序列来说可能更实用一些。发送一个列表,依次为每个动画制作动画,再次使用缩小列表再次调用动画。全部完成后执行回调。

此处的列表是选定的元素,但它可以是每个动画中包含不同动画参数的更复杂对象的列表。

Here is a fiddle

$(document).ready(function () {
    animate([$('#one'), $('#two'), $('#three')], finished);
});

function finished() {
    console.log('Finished');
}

function animate(list, callback) {
    if (list.length === 0) {
        callback();
        return;
    }
    $el = list.shift();
    $el.animate({left: '+=200'}, 1000, function () {
        animate(list, callback);
    });
}

答案 5 :(得分:2)

按顺序动画多个标签

如果你只选择像body一样的标签进行全局排队,你可以利用jQuery的内置动画排队:

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

所以,这就是为什么这个有效,以及它在做什么:

  1. $.globalQueue.queue()的调用只是对您的代码动画进行排队,但会将其排在正文标记上。

  2. 当jQuery在正文队列中点击您的标记动画时,标记的动画会在您的标记的队列中启动 - 但是jQuery动画框架的工作方式,任何自定义动画回调都会导致标记的动画队列(正文的在这种情况下)停止,直到自定义动画调用传入的dequeue()函数。因此,即使您的动画标记和正文的队列是分开的,正文标记的队列现在正在等待其dequeue()被调用。 http://api.jquery.com/queue/#queue-queueName-callback

  3. 我们只是通过调用其dequeue()函数来调用标记队列中的最后一个排队项目以继续全局队列 - 这就是将队列连接在一起的原因。

  4. 为方便起见,globalQueue.queue方法返回this引用以便于链接。

  5. 的setInterval

    为了完整起见,这里很容易找到setInterval的另一种选择 - 那就是你不是想要制作单独的动画坐标,因为随着时间的推移它们就会被激活而没有奇怪的浪涌由于新浏览器推迟动画队列和定时器以节省CPU的方式导致动画前进。

    您可以像这样替换setInterval的电话:

    setInterval(doAthing, 8000);
    

    有了这个:

    /**
     * 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/

    当背景标签的浏览器重新启用动画时,请避免那些尴尬的动画爆炸。

答案 6 :(得分:1)

您还可以将效果放入同一队列,即BODY元素的队列。

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

确保在最后一个效果回调中调用dequeue()。

答案 7 :(得分:1)

这已经得到了很好的解答(我认为jammus的答案是最好的)但我认为我会根据我在网站上的方式提供另一个选项,使用delay()函数...

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

(用你想要的动画速度替换1000.想法是你的延迟函数延迟了这个数量并累积每个元素动画的延迟,所以如果你的动画每500毫秒,你的延迟值将是500,1000,1500)

编辑:FYI jquery的'慢'速度也是600毫秒。因此,如果您想在动画中使用'慢',请在每次后续调用延迟函数时使用这些值 - 600,1200,1800

答案 8 :(得分:0)

我在考虑回溯解决方案。

也许,您可以定义此处的每个对象都具有相同的类,例如.transparent

然后,您可以创建一个函数,例如startShowing,该函数查找具有.transparent类的第一个元素,对其进行动画处理,删除.transparent,然后对其进行调用。

我不能保证顺序,但是通常遵循文档编写的顺序。

这是我尝试过的功能

function startShowing(){
      $('.pattern-board.transparent:first').animate(
        { opacity: 1}, 
        1000,
        function(){
          $(this).removeClass('transparent');
          startShowing();
        }
      );
    }

答案 9 :(得分:-8)

使用queue选项:

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