jQuery源码之动画详解

一.前言

动画在jQuery里属于重要的模块,库中很多模块都为它服务,queue队列模块,callbacks模块,缓存模块,下面是我自己看源码做笔记所写下来的,话不多说,一下是我自己所记录的,大部分代码做了笔记 。

我将整个动画分为2个部分,今天我来分析下动画变化之前,做了些什么,怎么实现动画的链式,queue队列到底做了什么

下次会讲动画真正实现的源码,不过今天的这个部分,我个人觉得比较有意义,也是动画的基础框架,一个简单的队列算法,却经过他人的技巧和经验,达到了自己想要的结果,这就是玩转算法吧!

html

#aaron{
		display:block;
		width:100px;
		height:30px;
		background:#eee;
		border:10px solid #ccc;
		padding:30px;
		;
	}

<div id="aaron">
	<div id="div">
		<p id="p">
			<a id='a' href="#">Aron test</a>
		</p>
	</div>
</div>

//	动画测试
	$("#aaron").animate({height:"300px",width:"440px"},1000,"swing",function(){
		console.log("Animation Show.")
	})/*.animate({height:"30px",width:"110px"},1000,"linear",function(){
		console.log("Animation Show.")
	})*/;
如果不明白动画API的自己可以去看手册,我就精简来讲下

animate函数

animate: function( prop, speed, easing, callback ) {
		
		//	Object {height: "hide", paddingTop: "hide", marginTop: "hide", paddingRight: "hide", marginRight: "hide"…} "slow" fn undefined
		var empty = jQuery.isEmptyObject( prop ),	//	false
		//	组装参数
			optall = jQuery.speed( speed, easing, callback );	//	Object {complete: function, duration: 600, easing: undefined, queue: "fx", old: function}
		
		//	每次动画函数都是把这个函数推入到queue中,只不过是prop,optall参数不同,产生不同的动画效果,其实还是一个一个动画执行,主不过可以链式写法
		var	doAnimation = function() {
				// arguments [function, Object]  上下文为elem
				//fn.call(next, hooks);
				// Operate on a copy of prop so per-property easing won't be lost
				//	div#aaron Object object
				//	这里执行动画的函数 elem,prop,optall				
				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
				
				// Empty animations, or finishing resolves immediately
				if ( empty || jQuery._data( this, "finish" ) ) {
					//	这段代码,好像没有执行
					anim.stop( true );
				}
			};
			
			doAnimation.finish = doAnimation;
		
		return empty || optall.queue === false ?
			this.each( doAnimation ) :
			this.queue( optall.queue, doAnimation );	//	this.queue('fx',doAnimation)  jQuery.fn.queue
	},

(1)optall = jQuery.speed( speed, easing, callback );
这句代码主要是把你传进来的动画参数,合并组装成一个对象
<pre name="code" class="javascript">jQuery.speed = function( speed, easing, fn ) {
	
	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
		complete: fn || !fn && easing ||
			jQuery.isFunction( speed ) && speed,
		duration: speed,
		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
	};
	
	//	这里讲duration从slow转换成600     jQuery.fx.speeds = Object {slow: 600, fast: 200, _default: 400}
	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
		
	//	默认动画队列名
	if ( opt.queue == null || opt.queue === true ) {
		opt.queue = "fx";
	}

	// Queueing
	opt.old = opt.complete;
	// luolaifa 第八个被推入的函数
	opt.complete = function() {
		if ( jQuery.isFunction( opt.old ) ) {
			//	this = div#aaron
			opt.old.call( this );
		}
		
		//	出列执行下一个动画
		if ( opt.queue ) {
			jQuery.dequeue( this, opt.queue );
		}
	};
	
	return opt;
};
根据传进来的参数,先初始化一些基本的属性,complete这个是将来当本动画完成所有回调的函数(其中主要是调用dequeue进行下一个动画)

 
duration动画时间,系统默认有3个速度值jQuery.fx.speeds = Object {slow: 600, fast: 200, _default: 400},如果没有穿对应的字符串,则是以你传进来的数值为动画时间(但是必须为数字)
easing是一个振幅参数,相当于数学当中的加速减速迅速的作用,默认为swing,还有一个linarn线性就是匀速
上面还给动画一个名称,opt.queue = fx ,如果没有传名称,则默认为fx
最后opt.old = opt.complete,就是用户自己传进来的回调函数,在complete中调用
最后返回一个组装好的参数对象
(2)doAnimation这个函数是传进queue队列的动画函数,是开始执行本次动画的入口
(3)this.queue( optall.queue, doAnimation ); // this.queue('fx',doAnimation)  jQuery.fn.queue

这里就是我们jQuery中的队列基础模块,这句是将动画函数进行插入队列尾部,并调用静态方法jQuery.dequeue出列执行

//	提供给用户调用的队列API
	queue: function( type, data ) {		//	fx doAnimation		
		var setter = 2;	//	存储和获取的标示符	
		
		//	type为function类型
		if ( typeof type !== "string" ) {
			data = type;
			type = "fx";
			setter--;
		}
		//	说明只传入了type一个参数,获取动画队列
		if ( arguments.length < setter ) {
			return jQuery.queue( this[0], type );
		}
		
		return data === undefined ?
			this :
			this.each(function() {
				//	div#aaron "fx"  doAnimation
				//	queue是一个回调函数队列   往缓存数据仓库里添加了 fxqueue属性/值为回调函数队列
				var queue = jQuery.queue( this, type, data );	//	jQuery内部静态API,供外部API服务
				
				// ensure a hooks for this queue	往缓存数据仓库里添加了 fxqueueHooks属性/值为系统回调函数队列
				//	该钩子的作用是当队列函数执行完毕或动画完成的时候,清理剩下的数据,回收内存空间
				jQuery._queueHooks( this, type );
				// 数据仓库的缓存    {fxqueue:Array[1],fxqueueHooks:Object,olddisplay:block}
				//	queue[0] = doAnimation
				//	如果是fx队列,则会自动出队列操作,inprogress是一个状态机,表示有动画函数正在执行
				if ( type === "fx" && queue[0] !== "inprogress" ) {
					jQuery.dequeue( this, type );
				}
			});
	},
(1)这个API,既可以存储动画函数(传二个参数),也可以取出动画函数(只传队列名),主要是setter变量,  
<pre name="code" class="javascript">//	说明只传入了type一个参数,获取动画队列
		if ( arguments.length < setter ) {
			return jQuery.queue( this[0], type );
		}
(2)
var queue = jQuery.queue( this, type, data );
<pre name="code" class="javascript">//	queue是一个回调函数队列   往缓存数据仓库里添加了 fxqueue属性/值为回调函数队列,<span style="font-family: Arial, Helvetica, sans-serif;">jQuery内部静态API,供外部API服务</span>

 
 


//内部调用	取出缓存队列
	queue: function( elem, type, data ) {

		//		div#aaron  "fx"   doAnimation
		var queue;

		if ( elem ) {
			type = ( type || "fx" ) + "queue";	//	fxqueue
			 
			 //	如果之前有fxqueue缓存数据,则获取,没有则直接返回undefined
			queue = jQuery._data( elem, type );
			
			// Speed up dequeue by getting out quickly if this is just a lookup
			if ( data ) {
				// 如果之前没有缓存数据,则用data数据 生成fxqueue缓存数据
				if ( !queue || jQuery.isArray(data) ) {
					
					queue = jQuery._data( elem, type, jQuery.makeArray(data) );
					
				} else {
					//	有则直接把data压入缓存数据仓库中
					queue.push( data );
				}
			}
			//	这个queue就是一个回调函数队列
			
			return queue || [];
		}
	},
(1)type = ( type || "fx" ) + "queue";	//	fxqueue
系统默认动画缓存属性 =  fxqueue,如果你传的动画名为myself,那么 缓存属性为 = myselfqueue

(2)queue = jQuery._data( elem, type );
主要在内部数据缓存中取出fxqueue对应的数据(一般doAnimation动画函数),如果之前没有存储过该缓存,那么返回undefined

(3)

if ( !queue || jQuery.isArray(data) ) {
					
					queue = jQuery._data( elem, type, jQuery.makeArray(data) );
					
				} else {
					//	有则直接把data压入缓存数据仓库中
					queue.push( data );
				}
如果没有对应的缓存数据,则传进来的data函数,放进fxqueue缓存仓库中,否则如果之前有该缓存的话,直接进行压栈

(4)return queue || [];<pre name="code" class="javascript">最后返回该队列

 
<pre name="code" class="javascript">jQuery._queueHooks( this, type );
 
往缓存数据仓库里添加了 fxqueueHooks属性/值为系统回调函数队列,<span style="font-family: Arial, Helvetica, sans-serif;">该钩子的作用是当队列函数执行完毕或动画完成的时候,清理本次动画剩下的数据,回收内存空间,</span>
<span style="font-family:Arial, Helvetica, sans-serif;">上面特意强调下本次动画,应为如果是链式动画的话,当每一个动画完成的时候都会清理一次,即清除当前动画的数据</span>
_queueHooks: function( elem, type ) {
		var key = type + "queueHooks";	//	fxqueueHooks
		
		return jQuery._data( elem, key ) || jQuery._data( elem, key, {
			empty: jQuery.Callbacks("once memory").add(function() {
				jQuery._removeData( elem, type + "queue" );	//	清空fxqueue队列数据
				jQuery._removeData( elem, key );			//	清空fxqueuehook队列数据
			})
		});
	}
缓存数据 属性= fxqueueHooks,,添加一个callbacks函数对象,调用add将

jQuery._removeData( elem, type + "queue" );

jQuery._removeData( elem, key );两个函数放进回调函数栈中, 动画完成后,激发并执行它们,清理数据(主要是缓存仓库中的动画队列数据)
	
jQuery.dequeue( this, type );
// 如果是fx队列,则会自动出队列操作,inprogress是一个状态机,表示有动画函数正在执行
 
//内部调用   从队列最前端移除一个队列函数,并执行
	dequeue: function( elem, type ) {
		//	div#aaron  "fx"   
		
		type = type || "fx";	// fx
		//	先取出队列中的函数
		var queue = jQuery.queue( elem, type );
		
		//	用来判断是否已全部出队列
		var 	startLength = queue.length,
		//	队列头删除一个
			fn = queue.shift(),
		//	每次运行一个动画,都要清空一次
			hooks = jQuery._queueHooks( elem, type ),	//	Object {empty: Object}empty: Objectadd: function () {disable: function () {disabled: function () {empty: function () {fire: function () {fireWith: function ( context, args ) {fired: function () {has: function ( fn ) {lock: function () {locked: function () {remove: function () {__proto__: Object__proto__: Object
		//	以便能够在回调函数中,循环执行出列操作
			next = function() {
				jQuery.dequeue( elem, type );
			};
		
		// inprogress 当作哨兵 如果是出列操作,总是先删除哨兵占位符
		// If the fx queue is dequeued, always remove the progress sentinel
		if ( fn === "inprogress" ) {
			fn = queue.shift();
			startLength--;
		}

		if ( fn ) {

			// Add a progress sentinel to prevent the fx queue from being
			// automatically dequeued
			//	当从queue队列头部剔除一个回调函数后,想队列头部增加'inprogress'
			//	用来做占位符作用,表示有动画函数正在执行
			if ( type === "fx" ) {
				queue.unshift( "inprogress" );
			}

			// clear up the last queue stop function
			delete hooks.stop;
			//	执行函数
			
			fn.call( elem, next, hooks );	//	在这里调用 doAnimation
		}
		
		//	如果queue队列出列完毕,则销毁缓存属性fxqueue,fxqueuehooks
		if ( !startLength && hooks ) {			
			hooks.empty.fire();
		}
	},
I(1)var queue = jQuery.queue( elem, type );<pre name="code" class="javascript">//	先取出队列中的函数队列

 

var 	startLength = queue.length,
		//	队列头删除一个
			fn = queue.shift(),
		//	每次运行一个动画,都要清空一次
			hooks = jQuery._queueHooks( elem, type ),	//	Object {empty: Object}empty: Objectadd: function () {disable: function () {disabled: function () {empty: function () {fire: function () {fireWith: function ( context, args ) {fired: function () {has: function ( fn ) {lock: function () {locked: function () {remove: function () {__proto__: Object__proto__: Object
		//	以便能够在回调函数中,循环执行出列操作
			next = function() {
				jQuery.dequeue( elem, type );
			};

<pre name="code" class="javascript">fn.call( elem, next, hooks );	//	在这里调用 doAnimation

 
这里将出列的函数执行,next是迭代dequeue函数,出列几个函数,就执行几次dequeue函数,一次只能出列一个,上面有一个占位符inprogress,当一个函数被出列操作,那么这个会被插入对列,顶戴它的位置,表示正在由动画函数执行中.<pre name="code" class="javascript">hooks 是清理数据的钩子,上面讲过每执行一次动画,就会清理一次,每执行一个动画,就会执行dequeue,出列下一个动画函数,并执行
其实这里的fn就是要执行的动画函数 doAnimation。
今天就到这,接下来就要将每个出队列的动画函数是怎么实现动画的,doAnimation就是入口.
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值