如何在不堆叠回调的情况下在 jQuery 中制作动画?

Posted

技术标签:

【中文标题】如何在不堆叠回调的情况下在 jQuery 中制作动画?【英文标题】:How do I animate in jQuery without stacking callbacks? 【发布时间】:2012-05-09 08:42:08 【问题描述】:

假设我有三个 div,我希望在前一个完成后每个都设置动画。目前,我写这个:

$('div1').fadeOut('slow', function() 
    $('div2').fadeOut('slow', function() 
        $('div3').fadeOut('slow');
    );
);

这很丑陋,但易于管理。

现在想象一下,我有 10 个不同的动画需要一个接一个地在不同的元素上发生。突然间,代码变得如此笨拙,以至于极难管理......

这是我想要做的伪代码:

$('div1').fadeOut('slow'  delay_next_function_until_done: true  );
$('div2').fadeOut('slow'  delay_next_function_until_done: true  );
$('div3').animate( top: 500 , 1000 );

我如何做到这一点?

【问题讨论】:

cdmckay.org/blog/2010/06/22/… 【参考方案1】:

如果您使用的是最新版本的 jQuery,请使用动画承诺:

$('div1').fadeOut('slow').promise().pipe(function() 
    return $('div2').fadeOut('slow');
).pipe(function() 
    return $('div3').animate( top: 500 , 1000 );
);

你可以让它通用:

$.chain = function() 
    var promise = $.Deferred().resolve().promise();
    jQuery.each( arguments, function() 
        promise = promise.pipe( this );
    );
    return promise;
;

var animations = $.chain(function() 
    return $('div1').fadeOut('slow');
, function() 
    return $('div2').fadeOut('slow');
, function() 
    return $('div3').animate( top: 500 , 1000 );
);

$.when( animations ).done(function() 
    // ALL ANIMATIONS HAVE BEEN DONE IN SEQUENCE
);

仍然有很多函数闭包,但这就是 javascript 的本质。但是,使用 Deferreds/Promises 更自然、更灵活,因为您避免了“初始”回调。

【讨论】:

简单干净 - 非常好:) pipe() 现在不推荐使用 then() -【参考方案2】:

我就是这样做的,用这个方法你可以随意放所有的div,只在list var中添加或删除元素,你也可以重新排序,不用担心延迟时间。

var list = [ '#div1', '#div2', '...' ];
var i = 0;
function fade(cb) 
    if (i < list.length) 
        $(list[i]).fadeOut('slow', function() 
            i++;
            fade(cb);
        );
     else 
        cb && cb();
    

fade();

您也可以在流程结束时添加回调

fade(function()alert('end'));

Demo

【讨论】:

@jfriend00 你的代码有一个同步选项的错误,当一个项目同步时你会一次抛出另一个序列,我给你举个例子来理解它list = [0-&gt;sync, 1-&gt;nosync, 2-&gt;sync, 3-&gt;nosync, 4-&gt;nosync, 5-&gt;nosync, 6-&gt;nosync, 7-&gt;sync] 它会运行0 1 (because 0 has sync) // 2 (throwed by 0), 3 (throwed by 1), 4 (because o2 has sync) // 5 (throwed by 2), 6 (throwed by 3), 7 (throwed by 4),我认为正确的运行将是 0 1 // 2 3 // 4 // 5 // 6 // 7 同步选项要求您正确识别同步操作和异步操作。因此.css()sync: true 而所有动画都不是。除此之外,我不确定您在说什么“错误”。如果这是生产代码,它可以列出所有已知的动画方法并自动检测它们,但我认为在我的答案中不合适。另外,为什么你把这个作为对你答案的评论,而不是我的(因为这个我几乎没有看到你的评论)? 但是在一个同步操作之后,你会同步抛出两个操作,但是如果你把下一个操作同步:false,这将被忽略,你将继续抛出接下来的两个操作,如果你添加另一个同步命令,然后将一次执行 3 个操作并继续 您似乎不明白sync 的用途。它仅用于像.css() 命令这样没有完成功能的立即运行的操作。当用于这些类型的操作时,它不会导致您描述的任何问题并且工作得很好。请参阅我的 jsFiddle,其中有一个不会导致您描述的任何问题的示例。您可以连续进行十个 sync 操作,只要它们实际上是同步操作(立即执行的操作),它就可以正常工作。不要将同步放在动画上。 Ups,阅读另一篇文章 xD,我知道您将其用于 css 方法,只是我误会了一个想法,对不起。但是我不会使用 css var,如果方法不是 jquery 效果,我会检测到它并使其同步,只是为了防止程序员使用 syn var。但是很抱歉说你有一个错误。【参考方案3】:

当完成函数或回调嵌套太深或代码一遍又一遍地重复时,我倾向于考虑使用通用函数的数据表解决方案:

function fadeSequence(list) 
    var index = 0;
    function next() 
        if (index < list.length) 
            $(list[index++]).fadeOut(next);
    
    next();


var fades = ["div1", "div2", "div3", "div4", "div5"];
fadeSequence(fades);

而且,如果您希望某些项目使用不同类型的动画,您可以创建一个对象数组来描述每个连续动画应该是什么。您可以根据需要在对象数组中添加尽可能多的细节。您甚至可以将动画与其他同步 jQuery 方法调用混合,如下所示:

function runSequence(list) 
    var index = 0;
    function next() 
        var item, obj, args;
        if (index < list.length) 
            item = list[index++];
            obj = $(item.sel);
            args = item.args.slice(0);
            if (item.sync) 
                obj[item.type].apply(obj, args);
                setTimeout(next, 1);
             else 
                args.push(next);
                obj[item.type].apply(obj, args);
            
        
    
    next();


// sequence of animation commands to run, one after the other
var commands = [
    sel: "#div2", type: "animate", args: [ width: 300, 1000],
    sel: "#div2", type: "animate", args: [ width: 25, 1000],
    sel: "#div2", type: "fadeOut", args: ["slow"],
    sel: "#div3", type: "animate", args: [ height: 300, 1000],
    sel: "#div3", type: "animate", args: [ height: 25, 1000],
    sel: "#div3", type: "fadeOut", args: ["slow"],
    sel: "#div4", type: "fadeOut", args: ["slow"],
    sel: "#div1", type: "fadeOut", args: ["slow"],
    sel: "#div5", type: "css", args: ["position", "absolute"], sync: true,
    sel: "#div5", type: "animate", args: [ top: 500, 1000]
];
runSequence(commands);

还有,这是第二个选项的工作演示:http://jsfiddle.net/jfriend00/PEVEh/

【讨论】:

大声笑,我认为这个概念是为了让它变得简单 第一个选项很简单。第二个可用于任意数量的对象的任何动画序列,而无需每次都编写任何新代码。您只需构建一个动态数据表。如果您更愿意手动编写我在第二个中显示的序列,请随意,但 OP 询问如何在没有所有嵌套回调的情况下执行此操作,因此我编写了一个通用引擎来处理它。还要记住,OP 询问了如何使其在不同对象上使用 10 个或更多连续和不同的动画。我没有看到任何其他解决方案可以解决这部分问题。【参考方案4】:

一种方法是编写自己的辅助函数,如下所示:

$.fn.sequentialFade = function() 
    if(this.length > 0) 
        var $set = $(this);
        $set.eq(0).fadeOut(function() 
            $set.slice(1).sequentialFade();
        );
    

然后像这样使用它:

$('.div1, .div2. .div3').sequentialFade();

http://jsfiddle.net/JpNgv/

【讨论】:

注意:这将按照 DOM 序列顺序淡化它们,不一定按照您列出它们的顺序,因为 jQuery 对象按 DOM 顺序排列它们的项目。【参考方案5】:

尝试类似:

$( 'div1' ).fadeOut();
$( 'div2' ).delay( 500  ).fadeOut();
$( 'div3' ).delay( 1000 ).fadeOut();

根据需要调整时间

【讨论】:

这似乎是一种非常笨拙的方法。另外,我认为延迟并不能保证这些事件会一致,尤其是在构建一系列事件时。 @Brad -- 实际上使用 jQuery 可以。 jQuery 使用最后一帧和当前帧之间的时间,所以除非浏览器锁定,否则它们只会关闭几毫秒。 他有 10 个元素。您可以使用$(...).each 并使用索引进行延迟,而不是写 10 次。 check this out【参考方案6】:

使用这个:

$('#div1, #div2, #div3').each(function(index)
    $(this).delay(1000 * index).hide(1000);
);

如果你能给&lt;div&gt;s 上课:

$('.forHide').each(function(index, value)
    $(this).delay(1000 * index).hide(1000);
);​
第一个元素在 1000 * 0 = 立即以一秒的动画淡出。 第二个元素在 1000 * 1 = 一秒,动画一秒后淡出。 第三个元素在 1000 * 2 = 两秒后淡出,动画为一秒。 ... ... n 元素在 1000 * n = n 秒后淡入,动画为一秒。

Live DEMO

【讨论】:

但是,这不能保证效果会在1000毫秒内完成。 @Starx。现在可以保证了。看看吧。 jquery 选择器是否保证返回顺序将是您在选择器中放置元素的顺序?只是好奇... @Endophage。它将按照它们在 DOM 中的顺序淡化它们。看看this fiddle @gdordon 所以如果你想让它们以与 dom 中不同的顺序淡入淡出,比如从下到上而不是从上到下,那么没有一种干净的方法可以做到这一点吗?跨度> 【参考方案7】:

回调是朋友,不要推开它。有一些方法可以简化它们。这是其中之一

$('div1').fadeOut('slow', div2)
function div3()  $('div3').fadeOut('slow'); 
function div2()  $('div2').fadeOut('slow', div3); 

【讨论】:

以上是关于如何在不堆叠回调的情况下在 jQuery 中制作动画?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用边框和图像的情况下在 div 上制作 CSS 三角形背景? [复制]

是否可以在不使用 socket.io 的情况下在节点中制作聊天应用程序

如何在不解析的情况下在javascript中同步包含JSON数据?

如何在不使用睡眠的情况下在 C# 中执行短暂的延迟?

有没有办法在不使用 jQuery UI 和 jQuery Mobile 的情况下在 jQuery 中左右滑动 [重复]

有没有办法在不使用 ImageMagick 或其他第三方软件的情况下在 R 中制作 GIF?