大概有多久沒(méi)有更新專欄文章了。半年?下半年忙到起飛,或者毫不夸張的說(shuō)是發(fā)射??jī)H有的一點(diǎn)個(gè)人時(shí)間,上半年貢獻(xiàn)給了Python,現(xiàn)在差不多已忘掉了七七八八(一首《涼涼》送給自己),下半年貢獻(xiàn)給了JavaScript,終于鼓起勇氣系統(tǒng)的開(kāi)始學(xué)習(xí)JS了(換臺(tái),梁靜茹《勇氣》走起)。本來(lái)想一直等等等,等到webapi學(xué)完后放個(gè)大招,svg+CSS動(dòng)畫搭乘上JavaScript簡(jiǎn)直如虎添翼長(zhǎng)驅(qū)直入。但是,看掘金作者群里討論的風(fēng)生水起,突然感覺(jué)自己全然是陌生人(此處應(yīng)放蔡健雅?),所以,更一篇文章刷一下存在感。
這篇文章本來(lái)是寫的《SVG+CSS動(dòng)畫》小冊(cè)中的一個(gè)小節(jié)選,小冊(cè)因?yàn)楦鞣N莫名的原因,擱淺了,擱淺……起航時(shí)間遙遙無(wú)期,但keyframes關(guān)鍵幀作為控制動(dòng)畫在不同時(shí)間的狀態(tài)的重要元素,決定了七十二變的終極形態(tài),所以這次更新專欄拿它下手。至于小冊(cè)嘛,如果能發(fā)的話,里面再換成其他的案例就好,此為后話。
關(guān)鍵幀keyframes的基礎(chǔ)概念此處可省略,下面才是滿滿的干貨。
案例:一路向前永不停歇的圓
因?yàn)橹皇莵?lái)解釋關(guān)鍵幀,所以只搞了一個(gè)簡(jiǎn)易的僅水平位移的動(dòng)效。因此,這篇文章得以脫離SVG單獨(dú)存在僅和CSS3動(dòng)畫屬性相關(guān)。

這是一個(gè)努力從起點(diǎn)滾向終點(diǎn)的圓圈,整個(gè)路程為800px,但我在它的必經(jīng)之路的三處設(shè)置了三個(gè)驛站。現(xiàn)在就要通過(guò)關(guān)鍵幀的定義讓圓圈在行進(jìn)的路上進(jìn)入驛站并稍作停頓。
1.先來(lái)一個(gè)最基礎(chǔ)的
CSS部分定義一個(gè)最基礎(chǔ)的位移動(dòng)畫,4s完成,線性速度case-關(guān)鍵幀演示1-基礎(chǔ) 。
@keyframes move{0%{transform:translateX(0)}100%{transform:translateX(800px)}}.c_move{animation:move 4s linear both} /*both:運(yùn)動(dòng)結(jié)束后停留在終點(diǎn)*/這個(gè)發(fā)揮作用主要是在定義了無(wú)限循環(huán)動(dòng)畫時(shí)。

基礎(chǔ)的設(shè)置自然帶來(lái)毫無(wú)特色平淡無(wú)奇的基礎(chǔ)動(dòng)效。
2.延遲開(kāi)始
下面,我想讓圓圈在起點(diǎn)停留2s后再開(kāi)始移動(dòng),第一反應(yīng)是用動(dòng)畫屬性中的延遲 animation-delay ,把時(shí)間定義成2s,行不行?行,但這里用個(gè)更高級(jí)的方法。我們?cè)诙x關(guān)鍵幀時(shí)用了大量的百分比,這里百分比值代表的是 時(shí)間節(jié)點(diǎn) ,也就是說(shuō) 關(guān)鍵幀定義的是不同時(shí)間節(jié)點(diǎn)的狀態(tài)屬性。 下面再來(lái)看一張圖,這張圖一定不要和上面的路徑演示弄混了,這是一張動(dòng)畫的時(shí)間軸的圖。

讓圓圈在起點(diǎn)停留2s那是表象說(shuō)法,轉(zhuǎn)化成我們的動(dòng)畫定義語(yǔ)言,就是在4s動(dòng)畫周期內(nèi)前2s是沒(méi)有動(dòng)畫效果的。所以,動(dòng)畫規(guī)則我來(lái)這樣定義:
@keyframes move{0%{transform:translateX(0)}50%{transform:translateX(0px)}100%{transform:translateX(800px)}}對(duì)照上面時(shí)間軸的分割來(lái)看,更容易理解一些,這樣就得到了在起點(diǎn)處停留2s后,在后面的2s完成整個(gè)動(dòng)畫的動(dòng)效。這里亦或用一種更簡(jiǎn)單的寫法為 0%, 50%{transform:translateX(0)} ,屬性相同的可以合并在一起,用逗號(hào)分隔。 case-關(guān)鍵幀演示-延遲開(kāi)始
3.提前結(jié)束
有了延遲開(kāi)始的基礎(chǔ),提前結(jié)束是不是已經(jīng)可以類推出來(lái)了。為了區(qū)分一下,我讓動(dòng)畫提前3s結(jié)束。照例先畫出時(shí)間軸的解析。

對(duì)應(yīng)關(guān)鍵幀的定義如下:
@keyframes move{0%{transform:translateX(0)}25%,100%{transform:translateX(800px)}}最終結(jié)果圓圈一定是4倍速度全力以赴加速完成旅程(畢竟要把原來(lái)4s的時(shí)間壓縮到1s完成),然后怡然自得的在終點(diǎn)等待整個(gè)動(dòng)畫時(shí)間結(jié)束。 case-關(guān)鍵幀演示-提前結(jié)束
4.中途停留
那些已準(zhǔn)備妥當(dāng)?shù)捏A站,現(xiàn)在可以發(fā)揮作用了,我希望圓圈這樣運(yùn)動(dòng):整個(gè)旅程中僅在第一個(gè)驛站(移動(dòng)200px后)停留1s,稍作整頓。映射到時(shí)間軸上是什么樣子的呢?

這里,出現(xiàn)了一些看上去很奇怪的數(shù)字,需要解釋一下。先來(lái)明確一點(diǎn),我們分析時(shí)間軸,最終要獲得是時(shí)間節(jié)點(diǎn)。針對(duì)我們的設(shè)計(jì),停留1s,那運(yùn)動(dòng)的時(shí)間就是3s,而這3s的時(shí)間是分成兩部分的,第一部分是前200px,第二部分是后600px,因?yàn)槭蔷€性勻速,所以當(dāng)時(shí)間軸分成A+B+C三部分后,在A時(shí)間段跑完200px,在C時(shí)間段跑完600px,計(jì)算出A對(duì)應(yīng)的時(shí)間0.75s,C對(duì)應(yīng)的時(shí)間2.25s,B的時(shí)間是停留的時(shí)間1s,再換算成對(duì)應(yīng)的百分比,這就是最終中間兩個(gè)時(shí)間節(jié)點(diǎn)的計(jì)算方法。時(shí)間軸解析完成后,CSS部分的定義手到擒來(lái):
@keyframes move{0%{transform:translateX(0)}18.75%, 43.75%{transform:translateX(200px)} /*對(duì)應(yīng)停留的1s*/100%{transform:translateX(800px)}}case-關(guān)鍵幀演示-第一個(gè)驛站停留1s
5.像蟲(chóng)洞一樣跳躍式前進(jìn)
增加些難度,在中途任意點(diǎn)作停留已經(jīng)不是什么問(wèn)題了,停留在一個(gè)點(diǎn)和多個(gè)點(diǎn)是相同的思路,現(xiàn)在,我讓圓圈跳躍式前進(jìn),進(jìn)入第一個(gè)驛站后,停留1s,跨過(guò)第二個(gè)驛站,直接進(jìn)入第三個(gè)驛站,停留1s,完成旅程。根據(jù)空間折疊原理,200和600處發(fā)生了躍遷。分析時(shí)間軸:

重點(diǎn)看紅色的部分,那里就是躍遷的時(shí)間點(diǎn),在無(wú)時(shí)間變化的情況下位移了200px。按照時(shí)間軸的分析,CSS部分理論上是這樣的:
@keyframes move{0%{transform:translateX(0)}25%,50%{transform:translateX(200px)}50%,75%{transform:translateX(600px)}100%{transform:translateX(800px)}}效果如何呢?

完全和想象的不一樣,問(wèn)題出在哪里呢?就是時(shí)間的50%節(jié)點(diǎn)處,瀏覽器可不知道你真實(shí)的想法,它只會(huì)以為你定義錯(cuò)了,當(dāng)有兩個(gè)相同的50%的關(guān)鍵幀的不同屬性值定義時(shí),會(huì)自動(dòng)忽略第一個(gè),而以最后一個(gè)有效值為準(zhǔn),所以上面的定義相當(dāng)于給瀏覽器傳達(dá)的訊息是這樣的:
@keyframes move{0%{transform:translateX(0)}25%{transform:translateX(200px)}50%,75%{transform:translateX(600px)} /*在位移600px后停留1s*/100%{transform:translateX(800px)}}這就是為什么看上去是到達(dá)第一驛站后加速跑向第三個(gè)驛站,然后停留后再完成剩下的路程的原因。現(xiàn)在游戲越來(lái)越有意思了,或許我們可以試試 騙過(guò)瀏覽器 。既然同樣的時(shí)間點(diǎn)只允許定義一個(gè)屬性值,那如果我在緊鄰的旁邊增加一個(gè)時(shí)間點(diǎn)來(lái)定義,會(huì)發(fā)生什么?
@keyframes move{0%{transform:translateX(0)}25%,50%{transform:translateX(200px)}50.0001%,75%{transform:translateX(600px)} 100%{transform:translateX(800px)}}看上面出現(xiàn)的**50.0001%**這個(gè)時(shí)間點(diǎn),猜猜發(fā)生了什么?這就是上面所謂的“騙過(guò)瀏覽器”的方法了。在50%→50.0001%這個(gè)區(qū)間,發(fā)生了400px(200→600)的位移變化。所以就得到了下面這種效果:case-關(guān)鍵幀演示-躍遷

從原理上來(lái)講,這是一種視覺(jué)欺騙,在極短的時(shí)間內(nèi)在兩個(gè)位置間發(fā)生位移,因?yàn)闀r(shí)間短到可以忽略,所以會(huì)有一種跳躍的假象。
總結(jié)
看完上面的幾種代表性實(shí)例,是不是對(duì)關(guān)鍵幀的定義有了一個(gè)全新的認(rèn)識(shí),你可能會(huì)覺(jué)得對(duì)于“延遲開(kāi)始”和“提前結(jié)束”這兩種需求,是完全可以通過(guò)定義延遲時(shí)間以及動(dòng)畫周期的時(shí)間來(lái)達(dá)到相同的效果的,但是,對(duì)于一個(gè)無(wú)限循環(huán)的動(dòng)效而言,延遲開(kāi)始永遠(yuǎn)只作用一次,當(dāng)動(dòng)畫一旦開(kāi)始進(jìn)入周而復(fù)始的循環(huán)后,不再支持這個(gè)屬性設(shè)置。因此,如果可以的話,盡量用關(guān)鍵幀的定義來(lái)完成。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。
新聞熱點(diǎn)
疑難解答
圖片精選