在海外項目中,爲了優化用戶體驗加入了幾處微交互動畫,實現方式是設計輸出合成的雪碧圖,前端經過序列幀實現動畫效果:
序列幀:
動畫效果:
序列幀:
幀動畫的缺點和侷限性比較明顯,合成的雪碧圖文件大,且在不一樣屏幕分辨率下可能會失真。經調研發現,Lottie是個簡單、高效且性能高的動畫方案。javascript
Lottie是可應用於Android, iOS, Web和Windows的庫,經過Bodymovin解析AE動畫,並導出可在移動端和web端渲染動畫的json文件。換言之,設計師用AE把動畫效果作出來,再用Bodymovin導出相應地json文件給到前端,前端使用Lottie庫就能夠實現動畫效果。css
靜態URL
https://cdnjs.com/libraries/l...html
NPM前端
npm install lottie-web
調用loadAnimationvue
lottie.loadAnimation({ container: element, // 容器節點 renderer: 'svg', loop: true, autoplay: true, path: 'data.json' // JSON文件路徑 });
也能夠在vue中使用lottiejava
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) } }
container | 用於渲染動畫的HTML元素,需確保在調用loadAnimation時該元素已存在 |
renderer | 渲染器,可選值爲'svg'(默認值)/'canvas'/'html'。svg支持的功能最多,但html的性能更好且支持3d圖層。各選項值支持的功能列表在此 |
loop | 默認值爲true。可傳遞須要循環的特定次數 |
autoplay | 自動播放 |
path | JSON文件路徑 |
animationData | JSON數據,與path互斥 |
name | 傳遞該參數後,可在以後經過lottie命令引用該動畫實例 |
rendererSettings | 可傳遞給renderer實例的特定設置,具體可看 |
Lottie提供了用於監聽動畫執行狀況的事件:node
可以使用addEventListener監聽事件git
// 動畫播放完成觸發 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跳到最後一幀。github
anm.goToAndStop(anm.totalFrames - 1, 1);
這樣的好處是能夠把相關聯的JSON文件合併,經過anm.goToAndPlay控制動畫狀態的切換,以下圖例中一個JSON文件包含了2個動畫狀態的數據:web
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功能。
除此以外,儘可能遵循官方文檔裏對設計過程的指導和建議:
因爲以上所說的功能支持問題會致使輸出動畫效果不肯定性,設計師和前端之間有個動畫效果聯調的過程,爲了提升聯調效率,設計師可先進行初步的效果預覽,再把文件交付給前端。
渲染前設置所要渲染的文件
勾選☑️Demo選項
在輸出的文件目錄中就可找到可預覽的demo.html文件
把生成的JSON文件傳到LottieFiles平臺,可播放、暫停生成文件的動畫效果,可設置圖層顏色、動畫速度,也能夠下載lottie preview客戶端在iOS或Android機子上預覽。
LottieFiles平臺還提供了不少線上公開的Lottie動畫效果,可直接下載JSON文件使用
Lottie的不足之處是沒有對應的API操縱動畫層,若想作更細化的動畫處理,只能直接操做節點來實現。好比當播放完左圖動畫進入驚訝狀態後,若想實現右圖隨鼠標移動而控制動畫層的簡單效果:
開啓調試面板能夠看到,lottie-web經過使用<g>標籤的transform屬性來控制動畫:
當元素已添加到DOM節點,找到想要控制的<g>標籤,提取其transform屬性的矩陣值,並使用rematrix解析矩陣值。
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
試了下的確能夠,以下圖,所以可經過這個方法快速找到須要操做的動畫層,進一步簡化代碼: