我們知道,做動畫有多種形式,可以用CSS的animation,也可以用Canvas,或者是用JS控制CSS的屬性形成動畫。我們經常使用CSS做一些比較簡單的動畫,像過度、載入的動畫,對於一些比較複雜的,可能會做成gif,或者是用Canvas,使用Canvas的控制粒度可以很細,同時工作量相對也比較大。做動畫還有其它的方式,那就是使用After Effect(AE)/ Flash/Premiere(Pr)/會聲會影等視訊軟體,這種視覺化的製作方式相對於直接寫程式碼來說,會更容易簡單自然。做動畫本身應該使用工具進行製作,但是這種視訊軟體做出來的動畫最後都是生成視訊檔案,並且通常體積還很大,沒有辦法直接移植到網頁上去。
然而好訊息是,現在我們可以使用AE做動畫,然後使用bodymovin外掛匯出成html檔案進行播放。AE是Adobe推出的一個很出名的視訊後期處理軟體,有些電影就是用AE做的,如變形金剛,還有人把AE當成加強版PS使用。也就是說假如我們可以AE做出一些電影級別的效果,然後用html播放,那是一件多麼酷炫的事情。
1. 安裝bodymovin
bodymovin是一個AE的一個開源的第三方擴充套件,github地址。上面可以下載這個外掛。然後再安裝一個ZXPInstaller來安裝這個檔案,然後重啟AE就可以了,當然前提是你要安裝一個AE。它支援AE CC版本:
After Effects | CC 2017, CC 2015.3, CC 2015, CC 2014 |
---|
安裝完之後,點選AE的選單Window -> Extensions -> Bodymovin就會彈出一個視窗:
2. 使用AE製作動畫
我相信很多人都沒有玩過AE,所以這裡我簡單地介紹一下。首先新建一個工程(project),然後新建一個合成(composition),選擇1080p/29fps,時長為10s,它就會建立一個10s的合成。如下時間軸皮膚的顯示:
這個時間軸將會是頻繁操作的地方。點選文字工具,在上方的預覽視窗選中一個位置點選建立文字,然後把它拖到視窗外面,因為我們準備做一個文字從外面進來的動畫,所以剛開始它是在外面的。把上圖右邊的藍色豎線表示的時間線拖到0s的位置,然後在左邊的文字圖層的Position屬性打一個關鍵幀,如下圖所示:
然後把時間線挪到3s的位置,改變文字的Position,把它挪到視窗的中間,這個時候AE會自動在時間線的位置打一個關鍵幀,如下圖所示:
然後按一下空格鍵進行預覽,預覽視窗就會播放起了我們剛剛設定的動畫:
你會發現,這個過程不是和CSS的keyframe動畫一樣的麼?沒錯!動畫的原理都是一樣,通過設定關鍵幀製作動畫。現在來比較一下用AE和用CSS/Canvas做這個動畫的區別。
3. 關鍵幀動畫
現在用CSS做這個動畫,如下程式碼所示:
<style>
.text{
animation: move 3s linear infinite;
}
@keyframes move{
from{
transform: translateX(-320px);
}
to{
transform: translateX(100px);
}
}
</style>
<div class="container">
<p class="text">Hello, frontend</p>
</div>複製程式碼
我們給animation新增一個動畫,這個動畫有兩個關鍵幀,分別在0%和100%的位置,需要變化的是transform的屬性。這段程式碼在瀏覽器執行,就會有剛剛用AE做的動畫的效果了。如果用Canvas呢,應該怎麼實現呢?
如下程式碼所示:
<canvas id="text-move" width="600" height="400"></canvas>
<script>
!function(){
window.requestAnimationFrame(draw);
var canvas = document.querySelector("#text-move"),
ctx = canvas.getContext("2d");
function draw(){
//計算文字position
var textPosition = getPosition();
drawText();
window.requestAnimationFrame(draw);
}
}();複製程式碼
這個是canvas動畫的基本框架,先註冊requestAnimationFrame的draw函式,使得瀏覽器在重新繪製螢幕時會先調一下這個函式,理想情況下1s會繪製60幅圖片,也就是說1s為60幀/60fps。
上面程式碼最關鍵的地方是在於計算文字位置position,同樣地,也是要先設定初始位置和終點位置還有動畫時間,從而知道移動的速度v,即每1s多少距離,記錄一個動畫開始時間,然後在每次draw的時候用Date.now()獲取當前時間減掉開始時間,就得到時間t,然後用v * t就可以得到位移。這就是用Cavans做動畫的基本原理,我們看到,用Canvas需要自己實現一個關鍵幀系統。
從抽象級別來看的話,AE > CSS >> Canvas,使用AE我只需要拖一拖,然後打上幾個關鍵幀,而使用CSS,我需要把我的操作寫成程式碼,而使用Canvas我需要從0開始一點一點去控制,當然你可以使用一些動畫和遊戲的引擎提高效率。所以如果有一個視覺化介面讓你去完成一些複雜的操作,和讓你一行一行去寫程式碼的方式選擇的話,我想大部分人應該會選擇前者。當然這兩者的區別不僅僅是操作上的簡便性,使用AE借用外掛還可以快速地製作出一些複雜的效果。
4. bodymovin小試牛刀
剛剛已經用AE做了一個最簡單的動畫,現在用bodywin把它匯出來。開啟bodymovin,選中合層,選擇輸出路徑,如下圖所示:
然後點選Render,完成它會輸出一個json檔案,開啟這個匯出的檔案:
{"v":"4.10.1","fr":29.9700012207031,"ip":0,"op":95.0000038694293,"w":1920,"h":1080,"nm":"Comp 1","ddd":0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Myriad Pro","fWeight":"","fStyle":"Regular","fName":"MyriadPro-Regular","ascent":70.9991455078125}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"hello, frontend","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[-1017,692,0],"e":[458,692,0],"to":[245.83332824707,0,0],"ti":[-245.83332824707,0,0]},{"t":90.0000036657751}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"t":{"d":{"k":[{"s":{"s":164,"f":"MyriadPro-Regular","t":"hello, frontend","j":0,"tr":0,"lh":196.8,"ls":0,"fc":[0,0.64,1]},"t":0}]},"p":{},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[]},"ip":0,"op":300.00001221925,"st":0,"bm":0}]}
這個檔案記錄了所有動畫的過程,如上加粗字型是我們剛剛打的兩個關鍵幀的位置。然後安裝一下bodymovin的引擎,可在github上面下載bodymovin.js或者是npm install一下:
npm install bodymovin複製程式碼
然後就可以使用bodymovin了,如下html:
<!DOCType html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="animation-container" style="width:100%"></div>
<script src="node_modules/bodymovin/build/player/bodymovin.js"></script>
<script src="index.js"></script>
</body>
</html>複製程式碼
index.js如下程式碼所示:
var animation = bodymovin.loadAnimation({
container: document.getElementById('animation-container'),
renderer: 'canvas', //svg、html
loop: true,
autoplay: true,
path: 'data.json'
})複製程式碼
呼叫它的loadAnimation的API,傳幾個引數,它支援canvas/svg/html三種形式,也就是說它可以用canvas做動畫,也可以用svg和html,其中canvas的效能最高,但是canvas有很多效果不支援。data.json的位置通過path告訴它。所有的動畫就通過改變path指向的data.json檔案區分,而其它的引數不用變。也就是說所有的動畫內容和效果都是通過data.json控制的。
現在在瀏覽器上面執行一下,你會發現報了一個錯:
後來發現這個錯誤是因為文字的原因,如果是用canvas的方式的時候要把文字導成svg的形式,而不是一個純文字然後通過設定font-family,這個可以在bodymovin裡面進行設定,如下圖所示:
還可以直接匯出一個完整的demo,直接開啟html就可以執行,這個比較方便。效果如下:
並且我們發現,它的大小和位移都是相對於容器的,當你把視窗拉小,它也會跟著變小。當使用svg的時候,它是用JS控制svg path標籤的transform:
當使用html時,它是控制CSS的transform:
我們一個hello, world的工程已經可以跑起來,bodymovin能支援多複雜的動畫呢?
5. AE的攝像機
用AE做動畫的時候經常會用到AE的攝像機圖層,所謂攝像機就是一個視角,預設情況下這臺攝像機是從正前方中間拍過去的,我們可以改變這臺攝像機的位置,如把攝像機往前推那麼內容就會放大,把攝像機往左右移動,那麼看到的內容就會發生傾斜,它有很多仿攝像機的引數可以控制:
攝像機屬性都可以通過打關鍵幀做動畫,現在我們加上攝像機做3d的動畫。做完後,如果還用canvas的話,它會提示你不能使用canvas,因為它目測不支援WebGL轉換,如下圖所示:
提示說使用了一個3d camera,嘗試使用html renderer,這裡要改成html。最後的效果如下:
通過檢查,可以看到攝像機也是用transform的matrix控制的。完整的dmo可見這個連結。
然後我們再繼續做複雜的動畫
6. 複雜動畫
在所有特效裡面,筆者最喜歡的是粒子效果,這種效果也是電影裡面經常用到的特效,如冰雪女王的冰雪魔法:
還有文字的粒子效果:
但是這種效果我試了一下沒有辦法匯出來,這種效果本身就比較複雜,渲染起來比較耗時,在html實時播放也不太現實。
還有有時候會報一些奇怪的錯誤,最常報的一個錯誤是這個:
bodymovin.js:9249 Uncaught TypeError: this.addTo3dContainer is not a function
可能是使用了一些特定效果,觸發了它的bug。
但是不要沮喪,我們還是可以匯出一些複雜的效果的,做動畫這種關鍵還是在於idea。
例如可以做一個裝飾的小動畫,如下所示:
一個完整的demo見這個網址。
還可以做一個相簿視訊,效果如下:
完整的demo見這個網址。
如果是做成一個1080p的視訊,1.5分鐘的mp4格式就算壓得比較厲害也要100多MB,但是現在我只要載入一個90kb的json檔案和一個240kB的庫檔案以及10Mb左右的圖片就可以了。並且裡面的文字和圖形還是向量的。
現在來看一下CPU消耗,可以看到這個相簿視訊svg動畫還是很耗CPU的,但是你開一個視訊播放器也同樣挺消耗CPU資源的。
不管怎麼樣,bodymovin提供了另外一種做網頁動畫的全新方式,擺脫那種純程式碼控制的黑暗,甚至你都不用學Canvas和WebGL,也可以做出很酷炫的動畫。但是無疑這種方式存在一種弊端,那就是沒辦法新增引數控制,例如我做一個憤怒的小鳥,我得通過拉弓的方向和力度以及小鳥的重量去計算它的軌跡。但是用AE做的只能是純動畫。所以平時可以兩者結合。
bodymovin還支援轉成IOS/Android程式碼,我感覺這個東西發展還在初級階段,網上也沒有很多關於這個的介紹。但是隨著這個的認可度提升,發展越來越好,說不定以後能夠支援更多的特效,甚至可以提供引數支援。