最近想写个动画库练练手,在写之前,先看了jquery动画方面的源代码,为此写篇博文记录一下心得体会。
概述
jquery动画很强大,它有一个很强大的特性:动画队列,比如:
1 | <button id="animation-test">test</button> |
示例:运行一下
可以看到,该按钮的动画按照调用animate
的顺序播放了!
其实在jquery动画中,个人认为,主要就是以下东西:
- 主循环
- 队列
- Tween
当然还有一些其他的,比如deferred等,不过这跟本文没多大关系,本文只讲jquery动画的骨架。
主循环
我们知道,动画都有一个过渡时间,叫duration
,而动画要做的事情,就是将duration
分解成一个一个小时间片段,这些分割点称之为frame
。
在动画中,最常用的时间分片手段就是setInterval
(本文不讲关于requestAnimationFrame的内容),jquery就是这么干的:
1 | var timerId = null; |
以上是摘自jquery的一段代码,它只用了一个定时器就搞定了所有动画,这个就是所谓的动画主循环,它是怎么做到的?
在Animation
这个方法中,有如下代码:
1 | // 帧处理函数 |
每次调用animate
方法都会进入到Animation
这个方法中,其实我们可以认为这里生成了一个动画对象。
在这里会把这个动画对象的tick方法放入到jQuery.timers
中,而上文所说的主循环就会定时扫描这个timers
数组,发现里面有tick就拿出来执行。
在动画主循环的每一帧中,会执行jQuery.timers
中的所有tick,从而使每个动画在每一帧中都得到一次执行更新的机会。
那什么时候会进入到Animation
这个方法中呢?看一下jquery动画入口方法animate
的实现:
1 | var doAnimation = function() { |
animate
只是对doAnimation
方法进行入列(原代码也有立即执行doAnimation
的分支,这里就不多讲),什么时候会执行doAnimation
呢?
队列
jquery对相同元素的动画进行了队列控制,并且它是把队列存放到DOM元素上的,在queue方法有以下代码:
1 | if(type === 'fx' && queues[0] !== 'inprogress') { |
入列动画任务时,jquery会判断队列头是不是inprogress
,如果不是,说明这个元素当前没有在播放动画。
这个时候,jQuery会取出队列的第一个动画任务进行播放,这个逻辑写在jQuery.dequeue
方法中:
1 | var fn = queue.shift(); |
PS:需要注意的是,jQuery有两个dequeue、queue方法,一个是在jQuery类上,一个是在jQuery的原型上(即jQuery.fn中)
以上代码需要注意的是inprogress
这个标志!顾名思义,它是用来标志该元素的队列中是不是有动画正在播放,jquery会在入列、出列的时候检查这个标志,以决定是否播放下一个动画。
还有一个问题:它什么时候播放下一个动画?当然是在上一个动画的complete回调中:
1 | // Queueing |
Tween
这个类其实逻辑不是特别复杂,jquery会对每个样式属性都创建一个Tween
对象,它主要是计算和更新元素的样式。
小结
以上就是阅读源代码后的一些理解,有不对之处,欢迎纠正/补充!
另,如果想阅读jquery源代码,推荐用github上的,上面的代码是模块化,传送门>>。