什么是逐幀動(dòng)畫
要了解 CSS3 逐幀動(dòng)畫,首先要明確什么是逐幀動(dòng)畫。
看一下維基百科中的定義:
定格動(dòng)畫,又名逐幀動(dòng)畫,是一種動(dòng)畫技術(shù),其原理即將每幀不同的圖像連續(xù)播放,從而產(chǎn)生動(dòng)畫效果。
簡(jiǎn)而言之,實(shí)現(xiàn)逐幀動(dòng)畫需要兩個(gè)條件:(1)相關(guān)聯(lián)的不同圖像,即動(dòng)畫幀;(2)連續(xù)播放。
我們兒時(shí)的記憶,手翻書,他所實(shí)現(xiàn)的就是逐幀動(dòng)畫:
(圖片來(lái)源:知乎)
前端逐幀動(dòng)畫實(shí)現(xiàn)方案
在細(xì)聊 css3 逐幀動(dòng)畫之前,我們先大致了解下前端實(shí)現(xiàn)逐幀動(dòng)畫有哪些方案。
其實(shí)不外乎三種技術(shù)(視頻可以實(shí)現(xiàn)所有類型的動(dòng)畫,暫不納入):gif、JavaScript、CSS3 Animation。
前文提到,實(shí)現(xiàn)逐幀動(dòng)畫需要兩個(gè)條件:(1)動(dòng)畫幀;(2)連續(xù)播放。
下面我們仔細(xì)自己分析下這三種技術(shù)是怎么實(shí)現(xiàn)上述條件的:
(1)gif
在觸屏頁(yè)中,gif 常被用來(lái)做加載動(dòng)畫。如《陌陌不孤獨(dú)飯局》的加載動(dòng)畫:
gif 可以有多個(gè)動(dòng)畫幀,連續(xù)播放是其自身屬性,是否循環(huán)也是由其本身決定的。它往往用來(lái)實(shí)現(xiàn)小細(xì)節(jié)動(dòng)畫,成本較低、使用方便。
但其缺點(diǎn)也是很明顯的:
- 畫質(zhì)上,gif 支持顏色少(最大256色)、Alpha 透明度支持差,圖像鋸齒毛邊比較嚴(yán)重;
- 交互上,不能直接控制播放、暫停、播放次數(shù),靈活性差;
- 性能上,gif 會(huì)引起頁(yè)面周期性的 paint ,性能較差。
(2)JavaScript
JS 與 CSS3,一般是將動(dòng)畫幀放到背景圖中。
不同的是,?JS 是使用腳本來(lái)控制動(dòng)畫的連續(xù)播放的:
-
可以直接改變?cè)氐?
background-image
-
也可以將動(dòng)畫幀合并成雪碧圖,通過(guò)改變?
background-position
?來(lái)實(shí)現(xiàn)
還是《陌陌不孤獨(dú)飯局》的例子:
其中有一個(gè)伸手取飯盒的動(dòng)畫,一共有19幀,且在第11幀處有一個(gè)交互,將雪碧圖放入背景中,通過(guò)不同的樣式實(shí)現(xiàn)不同的?background-position
?,使用 JS 改變樣式名:
使用 JS 的優(yōu)點(diǎn)是兼容性佳,交互靈活。
(3)CSS3 Animation
CSS3 實(shí)際上是使用?animation-timing-function
?的階梯函數(shù)?steps(number_of_steps, direction)
?來(lái)實(shí)現(xiàn)逐幀動(dòng)畫的連續(xù)播放的。
在移動(dòng)端,CSS3 Animation 兼容性良好,相對(duì)于 JS,CSS3 逐幀動(dòng)畫使用簡(jiǎn)單,且效率更高,因?yàn)樵S多優(yōu)化都在瀏覽器底層完成。
因此在觸屏頁(yè)面中 CSS3 逐幀動(dòng)畫使用廣泛,下文將對(duì)其進(jìn)行詳細(xì)介紹。
CSS3 逐幀動(dòng)畫的實(shí)現(xiàn)
(1)將動(dòng)畫幀合并為雪碧圖
在觸屏頁(yè)面中,動(dòng)畫往往承擔(dān)頁(yè)面樣式實(shí)現(xiàn)的角色(即不需要替換),因此我們會(huì)將圖片放到元素的背景中(background-image
)。
逐幀動(dòng)畫有不同的動(dòng)畫幀,我們可以通過(guò)更改?background-image
?的值實(shí)現(xiàn)幀的切換,但多張圖片會(huì)帶來(lái)多個(gè) HTTP 請(qǐng)求,且不利于文件的管理。
比較合適的做法,是將所有的動(dòng)畫幀合并成一張雪碧圖(sprite),通過(guò)改變?background-position
?的值來(lái)實(shí)現(xiàn)動(dòng)畫幀切換。因此,逐幀動(dòng)畫也被稱為“精靈動(dòng)畫(sprite animation)”。
以京東到家的觸屏頁(yè)面《年貨送到家》為例:
這個(gè)動(dòng)畫一個(gè)有三幀,將3個(gè)動(dòng)畫幀合并,并放到?.p8 .page_key
?的背景中:
(2)使用 steps 實(shí)現(xiàn)動(dòng)畫播放
steps 指定了一個(gè)階梯函數(shù),包含兩個(gè)參數(shù):
- 第一個(gè)參數(shù)指定了函數(shù)中的間隔數(shù)量(必須是正整數(shù));
- 第二個(gè)參數(shù)可選,指定在每個(gè)間隔的起點(diǎn)或是終點(diǎn)發(fā)生階躍變化,接受 start 和 end 兩個(gè)值,默認(rèn)為 end。
(參考自W3C)
通過(guò)W3C中的這張圖片來(lái)理解 steps 的工作機(jī)制:
回到上述的例子,我們?cè)?keyframes 中定義好每個(gè)動(dòng)畫幀:
為什么第一個(gè)參數(shù)是1?
前文中提到,steps 是?animation-timing-function
?的一個(gè)屬性值,在?W3C?中有如下說(shuō)明:
For a keyframed animation, the ‘a(chǎn)nimation-timing-function’ applies between keyframes, not over the entire animation.
也就是說(shuō),animation-timing-function
?應(yīng)該于兩個(gè) keyframes 之間,而非整個(gè)動(dòng)畫。在上面的 keyframes 中,我們已經(jīng)把每個(gè)幀都寫出來(lái)了,所以兩個(gè) keyframes 之間的間隔是1。
更加簡(jiǎn)便的寫法?
既然說(shuō) steps 第一個(gè)參數(shù)是指函數(shù)的間隔數(shù)量,那么我們就可以把 keyframes 的計(jì)算直接交給 steps 來(lái)完成。
以上兩種寫法效果是等同的。
CSS3 逐幀動(dòng)畫使用技巧
(1)step-start 與 step-end
除了?steps
?函數(shù),animation-timing-function
?還有兩個(gè)與逐幀動(dòng)畫相關(guān)的屬性值?step-start
?與?step-end
:
-
step-start
?等同于?steps(1,start)
:動(dòng)畫執(zhí)行時(shí)以開(kāi)始端點(diǎn)為開(kāi)始; -
step-end
?等同于?steps(1,end)
:動(dòng)畫執(zhí)行時(shí)以結(jié)尾端點(diǎn)為開(kāi)始。
(2)動(dòng)畫幀的計(jì)算:
(3)適配方案:rem+scale
我們知道,rem 的計(jì)算會(huì)存在誤差,因此使用雪碧圖時(shí)我們并不推薦用 rem。如果是逐幀動(dòng)畫的話,由于計(jì)算的誤差,會(huì)出現(xiàn)抖動(dòng)的情況。
那么在觸屏頁(yè)中,如何實(shí)現(xiàn)頁(yè)面的適配?
這里小編提供一個(gè)思路:
-
非逐幀動(dòng)畫部分,使用?
rem
?做單位; -
逐幀動(dòng)畫部分,使用?
px
?做單位,再結(jié)合?js
?對(duì)動(dòng)畫部分使用?scale
?進(jìn)行縮放。