4.5 并发与排队效果

通过刚才的例子,可以看出.animate()方法在为一组特定的元素创建并发效果时非常有用。然而,有的时候我们需要的则是排队效果,即让效果一个接一个地发生。

4.5.1 处理一组元素

当为同一组元素应用多重效果时,可以通过连缀这些效果轻易地实现排队。为了示范排队效果,我们仍以代码清单4-17为例,移动Text Size盒子、增加其高度、加宽其边框。不过,这次我们相继地执行这三个效果——很简单,只要把它们分别放在.animate()方法中并连缀起来即可,参见代码清单4-18。

代码清单4-18

  1. $(document).ready(function() {
  2. $('div.label').click(function() {
  3. var paraWidth = $('div.speech p').outerWidth();
  4. var $switcher = $(this).parent();
  5. var switcherWidth = $switcher.outerWidth();
  6. $switcher
  7. .css({position: 'relative'})
  8. .animate({left: paraWidth - switcherWidth}, 'slow')
  9. .animate({height: '+=20px'}, 'slow')
  10. .animate({borderWidth: '5px'}, 'slow');
  11. });
  12. });

虽然连缀允许我们把这两个.animate()方法放在同一行,但为了更好的可读性,这里故意将它们分开放在了各自的一行中。

通过使用连缀,可以对其他任何jQuery效果进行排队,而并不限于.animate()方法。比如说,我们可以按照下列顺序对<div id="switcher">上的效果进行排队。

  • 通过.fadeTo()将其不透明度减退为0.5。

  • 通过.animate()将其移动到右侧。

  • 通过.fadeTo()将其渐增回完全不透明。

  • 通过.slideUp()隐藏它。

  • 通过.slideDown()再将其显示出来。

我们所要做的,就是在代码中按照相同的顺序连缀这些效果,如代码清单4-19所示。

代码清单4-19

  1. $(document).ready(function() {
  2. $('div.label').click(function() {
  3. var paraWidth = $('div.speech p').outerWidth();
  4. var $switcher = $(this).parent();
  5. var switcherWidth = $switcher.outerWidth();
  6. $switcher
  7. .css({position: 'relative'})
  8. .fadeTo('fast', 0.5)
  9. .animate({left: paraWidth - switcherWidth}, 'slow')
  10. .fadeTo('slow', 1.0)
  11. .slideUp('slow')
  12. .slideDown('slow');
  13. });
  14. });
  • 越过队列

不过,要是想在这个<div>不透明度减退至一半的同时,把它移动到右侧应该怎么办呢?如果两个动画以相同速度执行,则可以简单地把它们组合到一个.animate()方法中。但这个例子中的.fadeTo()使用的速度字符串是'fast',而向右移动的动画使用的速度字符串是'slow'。在这种情况下,第二种形式的.animate()方法又可以派上用场了,参见代码清单4-20。

代码清单4-20

  1. $(document).ready(function() {
  2. $('div.label').click(function() {
  3. var paraWidth = $('div.speech p').outerWidth();
  4. var $switcher = $(this).parent();
  5. var switcherWidth = $switcher.outerWidth();
  6. $switcher
  7. .css({position: 'relative'})
  8. .fadeTo('fast', 0.5)
  9. .animate({
  10. left: paraWidth - switcherWidth
  11. }, {
  12. duration: 'slow',
  13. queue: false
  14. })
  15. .fadeTo('slow', 1.0)
  16. .slideUp('slow')
  17. .slideDown('slow');
  18. });
  19. });

第二个参数(即选项对象)包含了queue选项,把该选项设置为false即可让当前动画与前一个动画同时开始。

  • 手工队列

有关为一组元素应用排队效果的最后一个需要注意的问题,就是排队不能自动应用到其他的非效果方法,如.css()。下面,假设我们想在.slideUp()执行后但在.slideDown()执行前,把<div id="switcher">的背景颜色修改为红色,可以尝试像代码清单4-21这样来做。

代码清单4-21

  1. //未完成的代码
  2. $(document).ready(function() {
  3. $('div.label').click(function() {
  4. var paraWidth = $('div.speech p').outerWidth();
  5. var $switcher = $(this).parent();
  6. var switcherWidth = $switcher.outerWidth();
  7. $switcher
  8. .css({position: 'relative'})
  9. .fadeTo('fast', 0.5)
  10. .animate({
  11. left: paraWidth - switcherWidth
  12. }, {
  13. duration: 'slow',
  14. queue: false
  15. })
  16. .fadeTo('slow', 1.0)
  17. .slideUp('slow')
  18. .css({backgroundColor: '#f00'})
  19. .slideDown('slow');
  20. });
  21. });

然而,即使把修改背景颜色的代码放在连缀序列中正确的位置上,它也会在单击后立即执行。

把非效果方法添加到队列中的一种方式,就是使用.queue()方法。代码清单4-22就是在这个例子中使用.queue()方法的代码片段。

代码清单4-22

  1. $(document).ready(function() {
  2. $('div.label').click(function() {
  3. var paraWidth = $('div.speech p').outerWidth();
  4. var $switcher = $(this).parent();
  5. var switcherWidth = $switcher.outerWidth();
  6. $switcher
  7. .css({position: 'relative'})
  8. .fadeTo('fast', 0.5)
  9. .animate({
  10. left: paraWidth - switcherWidth
  11. }, {
  12. duration: 'slow',
  13. queue: false
  14. })
  15. .fadeTo('slow', 1.0)
  16. .slideUp('slow')
  17. .queue(function(next) {
  18. $switcher.css({backgroundColor: '#f00'});
  19. next();
  20. })
  21. .slideDown('slow');
  22. });
  23. });

像这样传递一个回调函数.queue()方法就可以把该函数添加到相应元素的效果队列中。在这个函数内部,我们把背景颜色设置为红色,然后又调用了next()方法,其返回的结果将作为参数传给回调函数。添加的这个next ()方法可以让队列在中断的地方再接续起来,然后再与后续的.slideDown ('slow')连缀在一起。如果在此不使用next()方法,动画就会中断。

 要了解有关.queue()方法的更多信息,读者可以参考http://api.jquery.com/category/effects/

在下面讨论多组元素的效果之后,我们会介绍另一种向队列中添加非效果方法的方式。

4.5.2 处理多组元素

与一组元素的情况不同,当为不同组的元素应用效果时,这些效果几乎会同时发生。为了示范这种并发的效果,我们可以在向上滑出一个段落时,向下滑入另一个段落。首先,要用到示例文档中的如下三、四段文本:

  1. <p>Fourscore and seven years ago our fathers brought forth
  2. on this continent a new nation, conceived in liberty,
  3. and dedicated to the proposition that all men are
  4. created equal.</p>
  5. <p>Now we are engaged in a great civil war, testing whether
  6. that nation, or any nation so conceived and so
  7. dedicated, can long endure. We are met on a great
  8. battlefield of that war. We have come to dedicate a
  9. portion of that field as a final resting-place for those
  10. who here gave their lives that the nation might live. It
  11. is altogether fitting and proper that we should do this.
  12. But, in a larger sense, we cannot dedicate, we cannot
  13. consecrate, we cannot hallow, this ground.</p>
  14. <a href="#" class="more">read more</a>
  15. <p>The brave men, living and dead, who struggled here have
  16. consecrated it, far above our poor power to add or
  17. detract. The world will little note, nor long remember,
  18. what we say here, but it can never forget what they did
  19. here. It is for us the living, rather, to be dedicated
  20. here to the unfinished work which they who fought here
  21. have thus far so nobly advanced.</p>
  22. <p>It is rather for us to be here dedicated to the great
  23. task remaining before us&mdash; that from these honored
  24. dead we take increased devotion to that cause for which
  25. they gave the last full measure of devotion&mdash; that
  26. we here highly resolve that these dead shall not have
  27. died in vain&mdash; that this nation, under God, shall
  28. have a new birth of freedom and that government of the
  29. people, by the people, for the people, shall not perish
  30. from the earth.</p>

接着,为了更清楚地看到效果发生期间的变化,我们为第三段和第四段分别添加1像素宽的边框和灰色的背景。同时,在DOM就绪时立即隐藏第4段,参见代码清单4-23。

代码清单4-23

  1. $(document).ready(function() {
  2. $('p').eq(2).css('border', '1px solid #333');
  3. $('p').eq(3).css('backgroundColor', '#ccc').hide();
  4. });

这样,示例文档会显示开始的段落,然后是read more链接和带边框的段落,如图4-11所示。

4.5 并发与排队效果 - 图1

图 4-11

最后,为第三段添加click处理程序,以便单击它时会将第3段向上滑(最终滑出视图),同时将第4段向下滑(最终滑入视图),参见代码清单4-24。

代码清单4-24

  1. $(document).ready(function() {
  2. $('p').eq(2)
  3. .css('border', '1px solid #333')
  4. .click(function() {
  5. $(this).slideUp('slow').next().slideDown('slow');
  6. });
  7. $('p').eq(3).css('backgroundColor', '#ccc').hide();
  8. });

通过截取到的这两个滑动效果变化过程中的屏幕截图,如图4-12所示,可以证实,它们确实是同时发生的。

4.5 并发与排队效果 - 图2

图 4-12

原来可见的第三个段落,正处于向上滑到一半的状态;与此同时,原来隐藏的第四个段落,正处于向下滑到一半的状态。

排队回调函数

为了对不同元素上的效果实现排队,jQuery为每个效果方法都提供了回调函数。同我们在事件处理程序和.queue()方法中看到的一样,回调函数就是作为方法的参数传递的一个普通函数。在效果方法中,它们是方法的最后一个参数。

当使用回调函数排队两个滑动效果时,可以在第3个段落滑上之前,先将第4个段落滑下。首先,我们看一看怎样通过回调函数设置.slideDown()方法,如代码清单4-25所示。

代码清单4-25

  1. $(document).ready(function() {
  2. $('p').eq(2)
  3. .css('border', '1px solid #333')
  4. .click(function() {
  5. $(this).next().slideDown('slow', function() {
  6. $(this).slideUp('slow');
  7. });
  8. });
  9. $('p').eq(3).css('backgroundColor', '#ccc').hide();
  10. });

不过,这里我们需要注意的是,必须搞清楚要滑上的到底是哪个段落。因为回调函数位于.slideDown()方法中,所以$(this)环境已经发生了改变。现在,$(this)已经不再是指向 .click() 的第三个段落了——由于 .slideDown() 方法是通过 $(this).next() 调用的,所以该方法中的一切现在都将$(this)视为下一个同辈元素,即第四个段落。因而,如果在回调函数中放入$(this).slideUp('slow'),那么我们最终还会把刚刚显示出来的段落给隐藏起来。

可靠地引用$(this)的一种简单方法,就是在.click()方法内部把它保存到一个变量中,比如var $ clickedItem = $(this)

这样,无论是在回调函数的外部还是内部,$clickedItem引用的都是第三个段落。使用了新变量之后的代码,参见代码清单4-26。

代码清单4-26

  1. $(document).ready(function() {
  2. $('p').eq(2)
  3. .css('border', '1px solid #333')
  4. .click(function() {
  5. var $clickedItem = $(this);
  6. $clickedItem.next().slideDown('slow', function() {
  7. $clickedItem.slideUp('slow');
  8. });
  9. });
  10. $('p').eq(3).css('backgroundColor', '#ccc').hide();
  11. });

 在.slideDown()的回调函数内部使用$clickedItem取决于闭包。我们将在附录A中详细地讨论这个重要但又不太好理解的闭包。

这次效果中途的屏幕截图如图4-13所示,第三段和第四段同时都是可见的,而且,第四段已经完成下滑,第三段刚要开始上滑。

既然讨论了回调函数,那么就可以回过头来基于代码清单4-22解决在接近一系列效果结束时改变背景颜色的问题了。这次,我们不像前面那样使用.queue()方法,而是使用回调函数,如代码清单4-27所示。

4.5 并发与排队效果 - 图3

图 4-13

代码清单4-27

  1. $(document).ready(function() {
  2. $('div.label').click(function() {
  3. var paraWidth = $('div.speech p').outerWidth();
  4. var $switcher = $(this).parent();
  5. var switcherWidth = $switcher.outerWidth();
  6. $switcher
  7. .css({position: 'relative'})
  8. .fadeTo('fast', 0.5)
  9. .animate({
  10. left: paraWidth - switcherWidth
  11. }, {
  12. duration: 'slow',
  13. queue: false
  14. })
  15. .fadeTo('slow', 1.0)
  16. .slideUp('slow', function() {
  17. $switcher.css({backgroundColor: '#f00'});
  18. })
  19. .slideDown('slow');
  20. });
  21. });

同前面一样,<div id="switcher">的背景颜色在它滑上之后滑下之前,变成了红色。注意,在使用交互的完成回调函数而不是.queue()时,不必在回调中调用next()

4.5.3 简单概括

随着在应用效果时需要考虑的变化的增多,要记住这些效果是同时发生还是按顺序发生会变得越来越困难。因此,下面简单的概括可能会对你有所帮助。

  • 一组元素上的效果:

    • 当在一个.animate()方法中以多个属性的方式应用时,是同时发生的;

    • 当以方法连缀的形式应用时,是按顺序发生的(排队效果)——除非queue选项值为false

  • 多组元素上的效果:

    • 默认情况下是同时发生的;

    • 当在另一个效果方法或者在.queue()方法的回调函数中应用时,是按顺序发生的(排队效果)。