若是你拿着一個疑問去找專業人士尋找答案,那麼你的一個疑問會變成三個,由於他會用另外兩個令你更加一頭霧水的名詞來解釋你的這個疑問。javascript
我想這是大多數,包括我在內,IT人在學習過程當中碰到的最大問題。當你有一段代碼或是一個概念不是很清楚,百度也好,Google也好,在論壇發問也好,給出的答案每每又會夾雜着更多你不懂得概念和令你頭疼的代碼。css
我亦是吃了一樣的虧,痛定思痛,決定對animate方面作一些總結,但願能給你們一些啓發和幫助html
從一個實際應用談起前端
今天不談animate()、fadeIn()、fadeOut()、slideUp()、show()、hide()諸如此類的具體動畫函數,而談談幾個並不經常使用的,甚至說是有點風馬牛不相及,但又十分十分重要的動畫函數queue(),dequeue(),和stop()。java
先讓咱們從一個簡單的例子談,假設有一個購物功能,在結帳以前,用戶仍然能夠把購物車裏的刪除至備選欄中(也許由於用戶的資金不足,能夠存儲至下次購買)jquery
好,根據以上描述的需求,咱們抽象瞭如下的佈局,"origin"模擬購物車,"goal"模擬備選欄,"object"模擬那個可能被刪除的物品,而"change"按鈕實現能夠把物品從左右的交換功能chrome
同時咱們但願當物品從左交換至右的過程當中,加入動畫效果,咱們但願是一個這樣過程:在左側先有一個hide()隱藏效果,告訴用戶物品已經再也不購物籃中,而後前端用appendTo()把物品移動至右側,在用一個show()效果告訴用戶已經轉移成功了。流程以下編程
(在左側)object隱藏——>object從左側轉移到右側——>object顯現(在右側)瀏覽器
因而很天然咱們得出瞭如下代碼:網絡
$(
'#object'
).hide(
'slow'
).appendTo($(
'#goal'
)).show(
'slow'
);
|
而且咱們將代碼付諸於實踐,以下所示:
本例完整代碼:
$(
'#test-change'
).toggle(
function
(){
$(
'#test-object'
).hide(
'slow'
).appendTo($(
'#test-goal'
)).show(
'slow'
);
},
function
(){
$(
'#test-object'
).hide(
'slow'
).appendTo($(
'#test-origin'
)).show(
'slow'
);
|
當你運行以後會發現,結果並不如咱們但願的那樣。object會先發生轉移,再隱藏,再出現:
object從左側轉移到右側 ——>object隱藏(在右側)——>object顯現(在右側)
因此問題是,明明是按咱們預約的順序書寫的,但結果卻不如指望的那樣,這是爲何?
這也是我在本身項目中第一個接觸queue()的實際例子,在jQuery的官方論壇發帖詢問緣由後(原帖在這裏),如下這段回答是關鍵
when you are using methods that animate content, those animations are added to what is called a queue, specifically the "fx" queue. normal jquery methods, such as prependTo(), are not added to the "fx" queue, therefore they get executed immediately instead of waiting till the previously added item in the queue is executed.
什麼意思呢?也就是說,當你使用一系列的動畫效果(如hide,show),這些動畫函數都會被放進一個名爲"fx"的隊列中,而後在以先進先出的方式執行隊列中的函數,而非動畫函數,好比上面例子中的appendTo函數,則是不會進入這個隊列中,而且先於動畫函數的執行,也就是在"fx"先進先出,取出第一個函數以前,它就已經執行了。
因此在上面的例子中,咱們能夠看到object被先轉移到了右側中,而後再隱藏再顯現,也就是appendTo函數先執行於動畫函數的結果
瞭解病根以後,咱們須要對症下藥——說到底只是個順序的問題,只要讓appenTo後於hide先於show執行就萬事大吉了。因而咱們想,可不能夠也讓appendTo函數加入隊列而且位於hide和show之間?這樣就能按順序執行了
答案是確定了,雖然"fx"隊列默認狀況下是儲備動畫的函數,但加入了manipulation函數也沒有什麼不能夠——準確來講不只僅是manipulation函數,jQuery爲咱們提供了queue()函數,來把你須要的某些代碼插入到某個隊列中。爲何說某個?由於在後面的例子中咱們能夠看到其實還能夠自定義隊列的。
咱們先把上面例子改爲咱們但願的效果,再根據代碼來說解queue的方法
$(
'#object'
).hide(
'slow'
).queue(
function
(next){
$(
this
).appendTo($(
'#goal'
));
next();
}).show(
'slow'
);
|
其實看代碼就一目瞭然。咱們能夠這麼理解(只是有助於理解它的功能,並不是它的實質),queue()就是不乖的,幫助插隊的函數,你想讓某個功能或某一系列功能插入幾個動畫之間,就把這一系列你想插入的功能函數放入queue()的函數體中,就向上面的代碼同樣,而參數"next"和next()則是保證再執行完這個插入的函數後,能繼續能執行隊列中的下一個動畫函數,在上面的例子中也就是show()。
修改代碼後,就能達到咱們要的效果了,以下所示:
本例完整代碼:
$(
'#test-change1'
).toggle(
function
(){
$(
'#test-object1'
).hide(
'slow'
).queue(
function
(next){
$(
'#test-object1'
).appendTo($(
'#test-goal1'
));
next();
}).show(
'slow'
);
},
function
(){
$(
'#test-object1'
).hide(
'slow'
).queue(
function
(next){
$(
'#test-object1'
).appendTo($(
'#test-origin1'
));
next();
}).show(
'slow'
);
});
|
.queue()初探
接下來咱們正經談談queue函數
咱們仍是從一個簡單的例子提及:
假如你要讓一個黑色背景的小方塊div,先收起(slideUp),在放下(SlideDown),背景再變成白色,語句應該怎麼寫?
吸收了上個例子的教訓,相信沒有會很天真的按順序寫出這樣的語句了吧?
$(
'div'
).slideUp(
'slow'
).slideDown(
'slow'
).css({
"background"
:
"red"
});
|
應該怎麼寫呢?使用queue函數!brilliant!
$(
'div'
).slideUp(
'slow'
).slideDown(
'slow'
).queue(
function
(next){
$(
'#object'
).css({
"background"
:
"red"
});
next();
});
|
實際例子就不在頁面上展現了,這是一段很簡單的代碼,應該能夠想象獲得吧。
在這裏我想說明幾個問題:
首先,jQuery官方在闡述.queue這個方法的時候有這麼一句話頗有趣:
This feature is similar to providing a callback function with an animation method,
but does not require the callback to be given at the time the animation is performed.
咱們又要回到.queue()的函數定義,其實咱們如今這種在queue中加入函數的用法,官方給出的函數原型是:
queue( [ queueName ], callback( next ) )
也就是說咱們加入的函數實際上是一個關於隊列的回調函數。也就是在隊列結束以後,系統會自動爲你調用你加入的函數。
插一句話,究竟什麼是回調函數?百度一下,你就知道。返回結果的第一條就是百度百科關於「回調函數」的解釋。可是正如本文章開頭所說,它的確給了你很詳細很詳細的解釋,但前提是你能消化那些C++專業詞彙和代碼……幸運的是個人Unix網絡編程老師(嘿,一位來自北大的博士)曾經給過咱們一個很通俗的解釋,本身定義,系統調用。回調函數的關鍵在於咱們沒法預知它什麼時候被調用。由於咱們只是定義了這麼一個函數,可能通知系統在完成某一系列的動做後來調用它。
其實咱們能夠這樣考慮,若是把這個函數做爲slideDown的回調函數效果不都是同樣的嗎?由於咱們最終想要的只是保證變色函數在slideDown以後執行,slideDown和queue的回調函數都能保證這種效果!look:
$(
'div'
).slideUp(
'slow'
).slideDown(
'slow'
,
function
(){
$(
'#object'
).css({
"background"
:
"red"
});
});
|
正是有殊途同歸之妙。
還有一點須要注意的是.queue()中的next參數和next()能不能捨去其一或是?
咱們上面說到queue中的函數是回調函數,若是咱們稍稍對上上面的代碼作一些修改,好比:
$(
'div'
).slideUp(
'slow'
).slideDown(
'slow'
).queue(
function
(next){
$(
'#object'
).css({
"background"
:
"red"
});
//next();
}).hide(
'slow'
);
|
一是我把next()語句註釋掉了,二是但願在變色之後再讓方塊隱藏起來。可是當你運行以後,發如今變色以後沒法對方塊執行隱藏。
要記住queue中的函數是回調函數呀,默認狀況下只有動畫隊列執行完了,纔會調用變色函數,既然動畫隊列都執行完了,哪裏來的hide()?因此next()是保證在執行完此次隊列後再次執行下一個動畫函數
我曾經嘗試過拋棄next參數而保留next()語句,這樣的結果是能在現代瀏覽器(firefox,chrome之類)中運行,但沒法在ie6中運行。因此,保留吧 。next和next()是jquery1.4中才開始出現的,而在以前使用的是.dequeue()函數,若是要將這節的例子改成使用dequeue(),以下:
$(
'#object'
).slideUp(
'slow'
).slideDown(
'slow'
).queue(
function
(){
$(
'#object'
).css({
"background"
:
"red"
});
$(
this
).dequeue();
});
|
自定義隊列
我以前有提過其實能夠不使用它默認的'fx'隊列,這節就教你們怎麼自定義一個屬於本身的隊列,很簡單:
我想創建一個名爲'custom'的隊列,裏面有一個能使黑色小方塊改變背景顏色的方法,以下:
$(
"div"
).queue(
"custom"
,
function
(next) {
$(
'div'
).css({
'background'
:
'red'
});
next();
});
|
所見即所得——前面一個'custom'表明新隊列的隊列名(要是我也取'fx'會怎麼樣?我也不知道,有興趣的朋友嘗試以後能夠留言告訴我結果,我也有興趣會知道),後面仍然是回調函數,把你想執行的功能堆砌進去。
但就這段代碼而已,待你真正添加進網頁,而且嘗試運行,會發現並不是「所見即所得」,壓根就不會有任何效果。由於我故意省略了一段最最關鍵的語句……修改後的以下:
$(
"div"
).queue(
"custom"
,
function
(next) {
$(
'div'
).css({
'background'
:
'red'
});
next();
})
.dequeue(
"custom"
);
//this is the key
|
對,就是這句,.dequeue("custom")。通常對與dequeue()的定義是「刪除隊列中最頂部的函數,而且執行它」。我並不贊同用「刪除」這個字眼,而是傾向於「取出」,其實這個函數的功能就好像是一個數據結構中隊列的指針,待隊列中前一個函數執行完後,取下一個隊列最頂端的函數。
實戰
OK,主要的幾個知識點都介紹完了。或許你會問,咱們真的會用到這麼複雜的動畫操做嗎呢?我相信大多數人士不會的,動畫在網頁中只是小小的點綴,可是當心駛得萬年船。在這裏我就照搬Cameron Mckay博客上的關於隊列應用的例子
假設你要這麼一個效果:讓一個物體向上浮動2000毫秒(2秒),而且在前1000毫秒物體徹底不透明,而在後1000毫秒物體從徹底不透明變成徹底透明,爲了解釋的更清楚,給出了下面的這個時間軸表(假設物體開始在距容器頂部100px的位置,也就是top:100px;):
時間(毫秒) | 距頂端高度 | 不透明度 |
---|---|---|
0 | 100px | 1.0 |
500 | 90px | 1.0 |
1000 | 80px | 1.0 |
1500 | 70px | 0.5 |
2000 | 60px | 0.0 |
從時間軸表中咱們能夠更清晰的看到咱們想要的效果,在這2000毫秒的時間內,物體的高度一致在均勻變化,逐漸減少,而不透明度在前1000毫秒始終保持爲1.0,而在後1000毫秒才逐漸減少直至徹底爲0。
若是咱們暫且只考慮向上浮動和透明效果,咱們可能會寫出這樣的語句:
$(
"#object"
).animate({opacity: 0, top:
"-=40"
}, {duration: 2000});
|
很遺憾,這樣的語句只能讓物體在總體2000毫秒中都處於逐漸向不透明轉化的過程,也就是不能讓它在前1000毫秒中保持100%不透明——因而咱們用queue來解決這個問題:
$(
"#object"
)
.delay(1000,
"fader"
)
.queue(
"fader"
,
function
(next) {
$(
this
).animate({opacity: 0},
{duration: 1000, queue:
false
});
next();
})
.dequeue(
"fader"
)
.animate({top:
"-=40"
}, {duration: 2000})
|
咱們先來看它的思路:把控制不透明度和控向上移動的動畫分別存儲在兩個隊列中,控制向上移動的隊列按默認狀況進行(在2000毫秒內完成),而不透明度的控制在1000毫秒內執行,但這個隊列要晚於默認隊列1000毫秒執行
再簡單一點,就是:前1000毫秒,只有控制高度的「fx」隊列執行,然後1000毫秒,控制不透明度的「fader」隊列和控制高度的「fx」並行
首先準備兩個隊列,
一個是默認的"fx",存儲高度變化動畫:
.animate({top:
"-=40"
}, {duration: 2000})
|
用來另外一個是自定義的"fader"的隊列,來存儲不透明度變化的動畫:
.animate({opacity: 0}, {duration: 1000, queue:
false
});
|
注意上面這段代碼中的"queue:false",這是很關鍵的一句話,目的是讓這個animate不進入默認的"fx"隊列中
任何的動畫效果都會進入"fx"隊列中,即便你定義在.queue()中的動畫也是同樣,而且動畫效果,務必會按順序執行,好比說下面這段代碼:
$(
'#object'
).slideUp(1000)<br> .slideDown(1000)<br> .animate({width:
'50px'
}, {duration: 1000});
|
運行後它只會按照順序來執行,先收起,再放下,再把寬度收縮爲50px
可是一旦我加入了"queue:false"這個參數:
$(
'#section3a'
).slideUp(1000)
.slideDown(1000)
.animate({width:
'50px'
}, {duration: 1000, queue:
false
});
|
你會發如今收縮放下的同時,object的寬度也在收縮
原本線性執行的slideUp,slideDown,animate,變成了animate和slideUp,slideDown並行:
運行結果以下
本例完整代碼:
OK,咱們回過頭來再看實戰中的這個例子:
$(
"#object"
)
.delay(1000,
"fader"
)
.queue(
"fader"
,
function
(next) {
$(
this
).animate({opacity: 0},
{duration: 1000, queue:
false
});
next();
})
.dequeue(
"fader"
)
.animate({top:
"-=40"
}, {duration: 2000})
|
其實前三個語句(這裏所說的語句以"."爲區分標誌),作了這麼幾件事:
定義一個名爲fader的隊列,專用於控制不透明度的改變——.queue()語句
讓它1000毫秒後執行——.delay()延時函數,延時fader隊列的執行時間;.dequeue執行fader隊列。
而最後的.animate則是默認進行的,不用管它。一塊兒來看看效果,左邊的是正確的,右邊的是錯誤的(可能在IE6中有佈局錯位的狀況,由於是jQuery例子,時間有限,也就不追究css的錯誤了吧……):
本例完整代碼:
好的,queue的主要功能就介紹到這裏,下面還有點時間,介紹一些非主流功能:
獲取隊列長度
好比用隊列名取得匹配元素的長度:
var
$queue=$(
"div"
).queue(
'fx'
);
|
很明顯,就是取得隊列名爲'fx'的隊列,若是想取得長度的話:
var
$length=$(
'div'
).queue(
'fx'
).length;
|
注意這裏的隊列長度只是匹配元素還未運行的隊列長度,當動畫運行完以後,隊列長度會自動歸爲0,舉下面一個例子:
function
animateT(){
$(
"#section2-div"
).slideToggle(
'3000'
)
.slideToggle(
'3000'
)
.hide(
'3000'
)
.show(
'3000'
)
.animate({left:
'+=200'
},2000)
.hide(
'3000'
)
.show(
'3000'
)
.animate({left:
'-=200'
},2000,animateT);
//在這輪動畫結束的時候再調用本身,使動畫無限循環下去
}
|
而後當點擊按鈕的時候顯示隊列的長度:
$(
"#section2-input"
).click(
function
(){
var
$queue=$(
"#section2-div"
).queue(
'fx'
);
$(
'#section2-h1'
).text($queue.length);
});
|
效果:
點擊按鈕就能夠看見實時隊列的長度
本例源碼:
替換隊列
queue還有一種用法是替換隊列,就是自定義一個隊列後,用自定義的隊列替換元素原匹配的隊列:
好比你給某個元素定義了兩個隊列:
$(
'div'
).queue(
'fx'
,
function
(){
$(
'div'
).slideDown(
'slow'
)
.slideUp(
'slow'
)
.animate({left:
'+=100'
},4000);
});
//定義fx
$(
'div'
).queue(
'fx2'
,
function
(){
$(
'div'
).slideDown(
'fast'
)
.slideUp(
'fast'
)
.animate({left:
'+=100'
},1000);
});
//定義fx2
|
這裏定義了兩個隊列,一個是慢隊列,也就是默認的'fx',另外一個是快隊列'fx2'
當點擊某個按鈕時:
$(
'input'
).click(
function
(){
$(
'div'
).queue(
'fx'
,fx2);
});
|
很簡單吧,明顯用fx2替換了fx,固然這也不是當即替換,若是fx尚未執行完的話。除非你用stop()函數(咱們下節課介紹)。
總結
OK,今天對queue 的講解就到這裏,確定有不少疏漏的地方,但願你們多多指證,上面的這些用法是我總結出來,應該算是比較主流的用法吧。若是還有一些我沒有提到,或是有什麼問題想交流,均可以留言給我。
參考的資料有jQuery官方文檔說明 ,Cameron McKay的博客,《犀利開發jQuery》
下節課我打算向你們介紹stop()函數,也是我栽過跟頭的地方。