如何在不堆叠回调的情况下在 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->sync, 1->nosync, 2->sync, 3->nosync, 4->nosync, 5->nosync, 6->nosync, 7->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);
);
如果你能给<div>
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数据?