Lottie-前端實現AE動效
專案中為了優化使用者體驗加入了幾處微互動動畫,過期的流程都是設計輸出合成的雪碧圖,前端通過序列幀實現動畫效果,如下圖動畫效果:

序列幀:

動畫效果:

序列幀:

幀動畫的 缺點和侷限性 比較明顯,合成的雪碧圖檔案大,且在不同螢幕解析度下可能會失真。經調研發現,Lottie是個簡單、高效且效能高的動畫方案。
Lottie 是可應用於Android, iOS, Web和Windows的庫,通過 Bodymovin 解析AE動畫,並匯出可在移動端和web端渲染動畫的 json檔案 。換言之,設計師用AE把動畫效果做出來,再用Bodymovin匯出相應地json檔案給到前端,前端使用Lottie庫就可以實現動畫效果。

Bodymovin外掛的安裝與使用
-
關閉AE
-
下載並安裝ZXP installer
-
下載最新版bodymovin外掛
github.com/airbnb/lott… -
把下載好的bodymovin.zxp拖到ZXP installer
-
開啟AE,在選單 首選項->常規 中勾選:ballot_box_with_check:允許指令碼寫入檔案和訪問網路(否則輸出JSON檔案時會失敗)
-
在AE中製作動畫,開啟選單視窗->拓展->Bodymovin,勾選要輸出的動畫,並設定輸出檔案目錄,點選render
開啟輸出目錄會看到生成的JSON檔案,若動畫裡匯入了外部圖片,則會在images中存放JSON中引用的圖片
前端使用lottie
靜態URL
cdnjs.com/libraries/l…NPM
npm install lottie-web 複製程式碼
呼叫loadAnimation
lottie.loadAnimation({ container: element, renderer: 'svg', loop: true, autoplay: true, path: 'data.json' }); 複製程式碼
vue-lottie
也可以在vue中使用lottie
import lottie from '../lib/lottie'; import * as favAnmData from '../../raw/fav.json'; export default { props: { options: { type: Object, required: true }, height: Number, width: Number, }, data () { return { style: { width: this.width ? `${this.width}px` : '100%', height: this.height ? `${this.height}px` : '100%', overflow: 'hidden', margin: '0 auto' } } }, mounted () { this.anim = lottie.loadAnimation({ container: this.$refs.lavContainer, renderer: 'svg', loop: this.options.loop !== false, autoplay: this.options.autoplay !== false, animationData: favAnmData, assetsPath: this.options.assetsPath, rendererSettings: this.options.rendererSettings } ); this.$emit('animCreated', this.anim) } } 複製程式碼
loadAnimation引數
container
用於渲染動畫的HTML元素,需確保在呼叫loadAnimation時該元素已存在
renderer
渲染器,可選值為'svg'(預設值)/'canvas'/'html'。svg支援的功能最多,但html的效能更好且支援3d圖層。 各選項值支援的功能列表在此
loop
預設值為true。可傳遞需要迴圈的特定次數
autoplay
自動播放
path
JSON檔案路徑
animationData
JSON資料,與path互斥
name
傳遞該引數後,可在之後通過lottie引用該動畫例項
rendererSettings
可傳遞給renderer例項的特定設定, 具體可看
Lottie動畫監聽
Lottie提供了用於監聽動畫執行情況的事件:
- complete
- loopComplete
- enterFrame
- segmentStart
- config_ready(初始配置完成)
- data_ready(所有動畫資料載入完成)
- DOMLoaded(元素已新增到DOM節點)
- destroy
可使用addEventListener監聽事件
// 動畫播放完成觸發 anm.addEventListener('complete', anmLoaded); // 當前迴圈播放完成觸發 anm.addEventListener('loopComplete', anmComplete); // 播放一幀動畫的時候觸發 anm.addEventListener('enterFrame', enterFrame); 複製程式碼
控制動畫播放速度和進度
可使用anm.pause和anm.play暫停和播放動畫,呼叫anm.stop則會停止動畫播放並回到動畫第一幀的畫面。
使用anm.setSpeed(speed)可調節動畫速度,而anm.goToAndStop(value, isFrame)和anm.goToAndPlay可控制播放特定幀數,也可結合anm.totalFrames控制進度百分比,比如可傳anm.totalFrames - 1跳到最後一幀。
anm.goToAndStop(anm.totalFrames - 1, 1); 複製程式碼
這樣的好處是可以把相關聯的JSON檔案合併,通過anm.goToAndPlay控制動畫狀態的切換,如下圖例中一個JSON檔案包含了2個動畫狀態的資料:

圖片資源
JSON檔案裡assets設定了對圖片的引用:

若想統一修改靜態資源路徑或者設定成絕對路徑,可在呼叫loadAnimation時傳入assetsPath引數:
lottie.loadAnimation({ container: element, renderer: 'svg', path: 'data.json', assetsPath: 'URL’// 靜態資源絕對路徑 }); 複製程式碼
功能支援列表
即使用bodymovin成功輸出了JSON檔案(沒有報錯),也會出現動效不如預期的情況,比如這是在AE中構建的形象圖:

但在頁面中渲染效果是這樣的:

這是因為使用了不支援的Merge Paths功能

因此對設計師而言,建立Lottie動畫和往常製作AE動畫有所不同,此文件記錄了Bodymovin支援輸出的AE功能列表,動畫製作前需跟設計師溝通好,根據動畫載入平臺來確認可使用的AE功能。
儘量遵循官方文件裡對設計過程的指導和建議:
- 動畫簡單化。建立動畫時需時刻記著保持JSON檔案的精簡,比如儘可能地繫結父子關係,在相似的圖層上覆制相同的關鍵幀會增加額外的程式碼,儘量不使用佔用空間最多的路徑關鍵幀動畫。諸如自動跟蹤描繪、顫動之類的技術會使得JSON檔案變得非常大且耗效能。
- 建立形狀圖層。將AI、EPS、SVG和PDF等資源轉換成形狀圖層否則無法在Lottie中正常使用,轉換好後注意刪除該資源以防被匯出到JSON檔案。
- 設定尺寸。在AE中可設定合成尺寸為任意大小,但需確保匯出時合成尺寸和資源尺寸大小保持一致。
- 不使用表示式和特效。Lottie暫不支援。
- 注意遮罩尺寸。若使用alpha遮罩,遮照的大小會對效能產生很大的影響。儘可能地把遮罩尺寸維持到最小。
- 動畫除錯。若輸出動畫破損,通過每次匯出特定圖層來調試出哪些圖層出了問題。然後在github中附上該圖層檔案提交問題,選擇用其他方式重構該圖層。
- 不使用混合模式和亮度蒙版。
- 不新增圖層樣式。
- 全屏動畫。設定比想要支援的最寬螢幕更寬的匯出尺寸。
- 設定空白物件。若使用空白物件,需確保勾選可見並設定透明度為0%否則不會被匯出到JSON檔案。
預覽效果
由於以上所說的功能支援問題會導致輸出動畫效果不確定性,設計師和前端之間有個動畫效果聯調的過程,為了提高聯調效率,設計師可先進行初步的效果預覽,再把檔案交付給前端。
方法1:輸出預覽HTML檔案
渲染前設定所要渲染的檔案

勾選:ballot_box_with_check:Demo選項

在輸出的檔案目錄中就可找到可預覽的demo.html檔案
方法2:LottieFiles分享平臺
把生成的JSON檔案傳到LottieFiles平臺,可播放、暫停生成檔案的動畫效果,可設定圖層顏色、動畫速度,也可以下載lottie preview客戶端在iOS或Android機子上預覽。

LottieFiles平臺還提供了很多線上公開的Lottie動畫效果,可直接下載JSON檔案使用

互動hack
Lottie的不足之處是沒有對應的API操縱動畫層,若想做更細化的動畫處理,只能直接操作節點來實現。比如當播放完左圖動畫進入驚訝狀態後,若想實現右圖隨滑鼠移動而控制動畫層的簡單效果:

開啟除錯面板可以看到,lottie-web通過使用

當元素已新增到DOM節點,找到想要控制的
onIntroDone() { const Gs = this.refs.svg.querySelectorAll('svg > g > g > g'); Gs.forEach((node, i) => { // 過濾需要修改的節點 ... // 獲取transform屬性值 const styleArr = node.getAttribute('transform').split(','); styleArr[0] = styleArr[0].replace('matrix(', ''); styleArr[5] = styleArr[5].replace(')', ''); const style = `matrix(${styleArr[0]}, ${styleArr[1]}, ${styleArr[2]}, ${styleArr[3]}, ${styleArr[4]},${styleArr[5]})`; // 使用Rematrix解析 const transform = Rematrix.parse(style); this.matrices.push({ node, transform, prevTransform: transform }); } } 複製程式碼
監聽滑鼠移動,設定新的transform屬性值。
onMouseMove = (e) => { this.mouseCoords.x = e.clientX || e.pageX; this.mouseCoords.y = e.clientY || e.pageY; let x =this.mouseCoords.x - (this.props.browser.width / 2); let y =this.mouseCoords.y - (this.props.browser.height / 2); const diffX = (this.mouseCoords.prevX - x); const diffY = (this.mouseCoords.prevY - y); this.mouseCoords.prevX = x; this.mouseCoords.prevY = y; this.matrices.forEach((matrix, i) => { let translate = Rematrix.translate(diffX, diffY); const product = [matrix.prevTransform, translate].reduce(Rematrix.multiply); const css = `matrix(${product[0]}, ${product[1]}, ${product[4]}, ${product[5]}, ${product[12]}, ${product[13]})`; matrix.prevTransform = product; matrix.node.setAttribute('transform', css); }) } 複製程式碼
進一步優化
看到一個方法,在AE中將圖層命名為**#id 格式,生成的SVG相應的圖層id會被設定為 id**,命名為**.class 格式,相應的圖層class會被設定為 class**

試了下的確可以,如下圖,因此可通過這個方法快速找到需要操作的動畫層,進一步簡化程式碼:

小結
Lottie的缺點在於若在AE動畫製作的過程不注意規範,會導致資料檔案大、耗記憶體和效能的問題;Lottie-web的官方文件不夠詳盡,例如assetsPath引數是在看原始碼的時候發現的;開放的API不夠齊全,無法很靈活地控制動畫層。
而優點也很明顯,Lottie能幫助提高開發效率,精簡程式碼,易於除錯和維護;資原始檔小,輸出動畫效果保真;跨平臺——Android, iOS, Web和Windows通用。
總的來說,Lottie的引用可以替代傳統的GIF和幀動畫,靈活利用好提供的屬性和方法可以控制動畫的播放,但需注意規範設計和開發的流程,才可以更高效地完成動畫的製作與除錯。
關注公眾號: 【IVWEB社群】 ,每週推送精品技術週刊 。