前言:
需要先看 jQuery源码解析之$
.queue()、$
.dequeue()和jQuery.Callbacks()
一、举例
divA
的宽度先变成 500px,再变成 300px,最后变成 1000px:
<script src="jQuery.js"></script>
<div id="A" style="width:100px;height:50px;background-color: deeppink">这是A</div>
<script>
let A = document.querySelector('#A');
//在异步调用中,进行同步调用
//动画是异步的
A.onclick = function() {
//就是连续调用animation.add()
$('#A').animate({
'width': '500'
}).animate({
'width': '300'
}).animate({
'width': '1000'
});
};
</script>
二、$
().animate()
作用:
通过 CSS 样式将元素从一个状态改变为另一个状态
源码:
//之前有说过: jQuery.fn.extend() 是$()的方法
jQuery.fn.extend( {
//源码8062行
//{'width': '500'}
animate: function( prop, speed, easing, callback ) {
//是否是空对象,false
var empty = jQuery.isEmptyObject( prop ),
// optall={
// complete:function(){jQuery.dequeue()},
// old:false,
// duration: 400,
// easing: undefined,
// queue:"fx",
// }
//undefined undefined undefined
optall = jQuery.speed( speed, easing, callback ),
doAnimation = function() {
// Operate on a copy of prop so per-property easing won't be lost
//Animation 方法执行单个动画的封装
//doAnimation的本质是执行Animation方法
//this:目标元素
//{'width': '500'}
//optall={xxx}
var anim = Animation( this, jQuery.extend( {}, prop ), optall );
// Empty animations, or finishing resolves immediately
//finish是数据缓存的一个全局变量
//如果为true,立即终止动画
if ( empty || dataPriv.get( this, "finish" ) ) {
anim.stop( true );
}
};
//注意这个
//自身的.finish=自身
doAnimation.finish = doAnimation;
return empty || optall.queue === false ?
this.each( doAnimation ) :
//一般走这里
//通过 queue 调度动画之间的衔接
//optall.queue:"fx"
//doAnimation:function(){}
this.queue( optall.queue, doAnimation );
},
})
解析:
(1)jQuery.speed()
作用:
初始化动画对象的属性
源码:
//源码8009行
//undefiend undefined undefined
//作用是返回一个经过修改的opt对象
jQuery.speed = function( speed, easing, fn ) {
// opt={
// complete:false,
// duration: undefined,
// easing: undefined,
// }
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
complete: fn || !fn && easing ||
isFunction( speed ) && speed,
duration: speed,
easing: fn && easing || easing && !isFunction( easing ) && easing
};
// Go to the end state if fx are off
/*这边是为opt.duration赋值的*/
//undefiend
if ( jQuery.fx.off ) {
opt.duration = 0;
} else {
if ( typeof opt.duration !== "number" ) {
if ( opt.duration in jQuery.fx.speeds ) {
opt.duration = jQuery.fx.speeds[ opt.duration ];
} else {
//走这边
//_default=400
opt.duration = jQuery.fx.speeds._default;
}
}
}
/*为opt.queue赋值*/
// Normalize opt.queue - true/undefined/null -> "fx"
//注意这个==,是模糊判断
if ( opt.queue == null || opt.queue === true ) {
opt.queue = "fx";
}
// Queueing
/*为opt.old赋值*/
opt.old = opt.complete;
opt.complete = function() {
if ( isFunction( opt.old ) ) {
opt.old.call( this );
}
//如果queue有值得话,就出队列
//因为queue默认为"fx",所以一般会触发dequeue操作
if ( opt.queue ) {
//this指目标元素
//opt.queue
jQuery.dequeue( this, opt.queue );
}
};
//此时的opt为:
// opt={
// complete:function(){jQuery.dequeue()},
// old:false,
// duration: 400,
// easing: undefined,
// queue:"fx",
// }
return opt;
};
解析:
通过传入的三个参数,对opt
对象进行处理,如果三个参数都为undefined
的话,opt
等于:
opt={
complete:function(){jQuery.dequeue()},
old:false,
duration: 400,
easing: undefined,
queue:"fx",
}
(2)animate
内部做了什么?
作用:
animate
内部封装了一个doAnimation
触发器,触发器触发就会运行Animation
方法,animate
最后返回的是queue()
方法,注意queue()
方法的参数带有触发器doAnimation
(3)$().queue()
作用:
执行入队操作(jQuery.queue()
),如果是fx动画的话,同时执行出队操作(jQuery.dequeue()
)
源码
这个方法上篇文章已经分析过了,这里就简单分析下:
jQuery.fn.extend( {
//optall.queue:"fx"
//doAnimation:function(){}
//fx动画的话,就执行dequeue(),也就是队首callback函数
queue: function( type, data ) {
//返回的是数组[doAnimate,doAnimate,xxx]
var queue = jQuery.queue( this, type, data );
//初始化hooks
jQuery._queueHooks( this, type );
/*专门为fx动画做处理*/
//如果队首没有锁的话,直接运行队首元素
if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
},
解析:
inprogress
是动画队列的锁,目的是保证上个动画执行结束后,再去执行下个动画
每入队一个doAnimate
函数,如果队首没有inprogress
锁的话,就会出队去运行一个doAnimate
函数
jQuery._queueHooks()
的意义在于添加一个empty.remove()
方法,用来清空队列queue
(4)jQuery.queue()
作用:
上篇文章也分析过了,就是将doAnimate
函数push
进queue
数组中
(5)jQuery.dequeue()
作用:
如果队首元素不是inprogress
,而是doAnimation
方法,则先将doAnimation
出队,再让inprogress
入队首,再运行doAnimation
方法;
如果队首元素是inprogress
的话,则移除锁
如果队列为空的话,则清空queue
,节省内存
源码:
//源码4624行
//目标元素,'type'
dequeue: function( elem, type ) {
//'type'
type = type || "fx";
//get,获取目标元素的队列
var queue = jQuery.queue( elem, type ),
//长度
startLength = queue.length,
//去除对首元素,并返回该元素
fn = queue.shift(),
//确保该队列有一个hooks
hooks = jQuery._queueHooks( elem, type ),
//next相当于dequeue的触发器
next = function() {
jQuery.dequeue( elem, type );
};
// If the fx queue is dequeued, always remove the progress sentinel
//如果fn='inprogress',说明是fx动画队列正在出队,就移除inprogress
if ( fn === "inprogress" ) {
fn = queue.shift();
startLength--;
}
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
//如果是fx动画队列的话,就添加inprogress标志,来防止自动出队执行
//意思应该是等上一个动画执行完毕后,再执行下一个动画
if ( type === "fx" ) {
queue.unshift( "inprogress" );
}
// Clear up the last queue stop function
//删除hooks的stop属性方法
delete hooks.stop;
//递归dequeue方法
fn.call( elem, next, hooks );
}
console.log(startLength,'startLength4669')
//如果队列是一个空数组,并且hooks存在的话,清除该队列
if ( !startLength && hooks ) {
//进行队列清理
hooks.empty.fire();
console.log(hooks.empty.fire(),'bbbb4671')
}
},
解析:
循环同步运行多个doAnimation()
方法,直到队列为空
综上,除doAnimation
内的逻辑外,整个$().animate()
的流程图为:
网友评论