jQuery中处理动画序列引起的问题
jQuery中的动画效果应该是相当出色的,让我们能够以非常简单的代码来实现很多复杂的效果,比如show()、hide()、slideDown()、slideUp()等。另外,如果给同一个元素指定不同的动画效果,那么这些动画效果是按照序列来执行的,比如如下这段代码:
$(document).ready(function(){
$('#panel').css('opacity','0.5');
$('#panel').click(function(){
$(this).animate({
left:'500px',
height:'200px',
opacity:'1'
},3000).animate({
top:'200px',
width:'200px'
},3000).css('border','5px solid blue');
});
});
});
代码先将#panel这个元素的透明度设置为0.5,之后元素会先向右移动500px、高度变为200px、透明度变为1,再然后元素会在第一段动画完成后,向下移动200px,并且高度变为200px。这两段动画其实就组成了一个动画序列,先执行第一段,然后执行第二段。
不过有时候,这种有动画序列却会引起一些问题,比如我想用jQuery中的slideDown()和slideUp()来制作有滑动效果的下拉菜单:鼠标悬浮于导航的时候菜单滑下,鼠标离开的时候菜单滑上,可是如果不作任何处理,把鼠标快速地在几个导航上快速滑入滑出,菜单节奏就会乱掉,上上下下像在跳舞一下:
解决方法一:使用jQuery带的stop()方法
jQuery自带的stop()方法的描述是这样的
stop( [clearQueue] [, gotoEnd] )
两个参数均为可选,它们都是布尔值
clearQueue表示是否清空未执行完的动画序列
gotoEnd表示是否将正执行的动画跳转到末状态
如果两个参数均不设置,则它会立即停止当前元素的动画。如果动画序列中还有其他的动画,则会以当前状态开始执行接下来的动画
那么想想这个菜单的实际情况:鼠标移入的时候,实际它就会在动画序列里添加一个slideDown动画,然后在鼠标移出的时候再添加一个slideUp动画。因为动画是按顺序执行的,所以第二个动画非得等到第一个动画执行完毕了以后再执行。如果鼠标移入移出的速度过快,动画就会慢慢按序列里的顺序执行直到完毕,所以就会出现DEMO1中的效果。那么用stop()的方法来清空序列不就行了?是的,所以把stop()方法的第一个参数设置为true。
那第二个值呢?我们想想,我们的目标是把菜单完全展示于用户的面前,并且slideUp动画是需要基于之前slideDown的状态来完成的,你slideDown把菜单的整个高度都显示出来了,slideUp才好把菜单隐藏掉。另外还需要注意的一点是,slideUp和slideDown动画未执行完的时候,会把过程中的临时变量记录到DOM中,也就是说,如果你指定第二个参数为false,那么比如菜单高度有100px,但因为鼠标移出提前结束了,只展开了10px,那么菜单的高度就是10px并且被记录在DOM中了。再次使用slideDown的话,它就会以10px为基础展开,显然菜单并不会完整地展示于用户有面前。所以把第二个参数也设置为true,让它让元素动画直接跳转到末状态。
大家可以把鼠标快速地在导航上滑入滑出试试看,虽然动画没有什么问题,但是能明显看到,如果在菜单展开的过程中移出了鼠标,菜单会立刻全部展开到末状态,而不是慢慢展开。关键代码如下
$(document).ready(function(){
$('#menu > li').hover(function(){
//这里的stop(true,true)指的是让动画队列清空,并且让已经在进行动画的元素到达动画的末状态,方便在第二次动画中使用
$(this).find('ul').eq(0).stop(true,true).slideDown('slow');
},function(){
$(this).find('ul').eq(0).stop(true,true).slideUp('slow');
});
});
解决方法二:使用setTimeout来判断鼠标悬停的时间
这种方法是mg12提出来的,感觉比第一种方法更好,菜单滑动的时候不突兀。关键代码如下:
$(document).ready(function(){
//设定两个数组,分别对应在不同元素上鼠标滑入和滑出要触发的函数
//鼠标滑入的数组
var mouseover_tid = [];
//鼠标滑出的数组
var mouseout_tid = [];
//使用each()方法,index为此方法自动传入的参数,记录匹配元素在元素集合中的所处位置
$('#menu > li').each(function(index){
$(this).hover(function(){
//this为当前的li元素,这里赋给self变量,方便在setTimeout方法中引用
var self = this;
//先清除鼠标滑出时的slideUp方法
clearTimeout(mouseout_tid[index]);
//鼠标滑入时,把当前的鼠标滑放的方法记录到数组中,并判断鼠标停留的时间是否符合条件
mouseover_tid[index] = setTimeout(function(){
//如果这里不用self而用this,this指向的就不是当前的li这个DOM元素
$(self).find('ul').eq(0).slideDown('slow');
},150);
},function(){
//鼠标滑出时的说明与滑入时的说明相似,此处省略
var self = this;
clearTimeout(mouseover_tid[index]);
mouseout_tid[index] = setTimeout(function(){
$(self).find('ul').eq(0).slideUp('slow');
},150);
});
});
});
代码的关键位置我已经注释清楚了,你也可以去mg12的那篇文章里看看。与第一种方法的区别在于,由于没使用stop()方法,所以你不会看到很突兀的鼠标一移开,菜单马上全展开的时候的效果。
结语
这里只是用slideDown和slideUp举的例子,方法也可以适用于其他的情况,比如fadeIn和fadeOut、animate等。目前只介绍这两种方法,以后想到了再补充,另外,如果大家有更好的意见,欢迎反馈。
已经有2次占座了,你也来凑个热闹吧
杀勒个发 —_—!
一般人不怎么注意这点,下拉什么的加个stop还是蛮有必要的~
@林木木:我以前基本是乱用stop(),不过现在理解了,会用得更合理一些吧
我这里写这样的文章,就慢慢冷清了,哈哈