前言:
須要先看 jQuery源碼解析之$
.queue()、$
.dequeue()和jQuery.Callbacks()javascript
1、舉例divA
的寬度先變成 500px,再變成 300px,最後變成 1000px:java
<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>
複製代碼
2、$
().animate()
做用:
經過 CSS 樣式將元素從一個狀態改變爲另外一個狀態web
源碼:數組
//以前有說過: 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()
做用:
初始化動畫對象的屬性緩存
源碼:app
//源碼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()
)動畫
源碼
這個方法上篇文章已經分析過了,這裏就簡單分析下:ui
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()
的流程圖爲: