導語 本文主要挖掘、弄懂lottie動畫背後的原理。lottie動畫是如何讓30FPS流暢運行?css
工做以來,處理過css、js、canvas、骨骼動畫,這些背後的原理都是經過把每一幀(瞬間)的靜止圖像組合起來,以必定頻率(速率)播放這些圖像造成動畫。json
詳細一點解析:動畫能夠拆分紅每一幀,當前幀(靜態)圖像的屬性數據或者形態(形狀)的變動,把這樣不少幀連貫起來,就造成動畫。canvas
原理其實也是這樣。但爲何它能作到如此流暢,讓衆多用戶,開發,設計師情有獨鍾。下面從幾個方面對lottie進行剖析:微信
用AE(Adobe After Effects)製做動畫,結合bodymovin插件把動畫導出json文件,網上不少導出案列,這裏再也不展開。而後加載lottie庫和下面幾行代碼就能夠實現一個lottie動畫。markdown
//animationData AE導出的數據源json
let params = {
container: document.getElementById('lottie'),
renderer: 'canvas',
loop: true,
autoplay: true,
animationData: animationData
};
let anim;
anim = lottie.loadAnimation(params);
複製代碼
下面對json數據進行分析。左右數據一一對應。oop
fr:幀率,就是用AE作動畫的時候已經預設好,這是多少幀率(fps)的動畫。動畫
ip、op:開始、結束幀。0幀開始,180幀結束,因此這個動畫總的運行時間是6秒。由於fps是30幀/秒,因此180幀對應6秒。ui
assets:動畫所需的圖像資源信息。spa
layers:動畫層信息(這裏的數據就是整個lottie動畫的核心)下面詳細說明一下:插件
層級的關鍵信息是ks(層級數據信息): 以下圖所示。這5個是動畫中最基本的屬性,也是最重要的。
而每一個屬性數據中,k的值就是固定數字(靜態)或者是組合的關鍵幀數據集合(動態),而這組合的關鍵幀數據就是lottie動畫須要的數據。
下面舉一個列子說明這個關鍵幀數據的集合是什麼?
如上圖所示,其實製做的時候就是2個關鍵幀(控制scale屬性的數據),導出的數據以下。
(圖一)
上面的數據的翻譯:0至30幀 scale屬性值從20 變到 12,30至60幀 scale屬性值從12 變到 20,完成動畫。上面動畫之因此一直動,是lottie庫能夠設置loop循環播放。
上面已經把json數據分析清楚,但lottie是如何去運行這些數據,而又可以讓AE導出30fps的動畫流暢渲染。
其實lottie是用了 requestAnimationFrame----在於充分利用顯示器的刷新機制,比較節省系統資源。顯示器有固定的刷新頻率(正常狀況下能達到60fps) 去渲染每一幀。這裏你可能會有疑惑,明明是30fps,爲何你要用60fps去渲染。
一開始個人思考,用60fps去渲染能保證動畫更流暢,可是問題來了。假設動畫只有30幀,製做的時候整個動畫總時間1s。可是用60fps去渲染,用0.5s就完成整個動畫播放。下面是我求知的過程:
因此下面說一下lottie的處理方式:
假設json數據如圖一,整個動畫就是一共60幀,播放速度是30fps/s。2個關鍵幀 0 ->30->60。sclae屬性的變化從20->12->20
lottie的核心原理是這樣的:
上面只是勻速狀態下的屬性值,大多數狀況下,動畫的速率都是變化的。其實圖一還有一些數據就是用做速率的,就是圖一中的 o 和 i 數據。其中 o 和 i 能夠是2個貝塞爾--速率控制點。而2個控制點的貝塞爾曲線屬於3階貝塞爾曲線,能夠代入三階公式
或者用BezierFactory(文章最後會展現)封裝好的庫,用當前時間幀佔比值(0到1),去得出屬性值佔比,從而求出以某種貝塞爾速率當前幀下的屬性值。下面是關鍵代碼
if (currentFrame >= keyData.t && currentFrame <= rotate[i+1].t) {
dis_x = rotate[i+1].t - keyData.t; //時間差
dis_y = keyData.e - keyData.s; //數值差
if (!layer.fnc[i]) {
layer.fnc[i] = BezierFactory.getBezierEasing(keyData.o.x, keyData.o.y, keyData.i.x, keyData.i.y, keyData.n).get;
}
// layer.fnc[i] 是用來求當前幀時間佔比下速率所對應的屬性值佔比
perc = layer.fnc[i]((currentFrame - keyData.t) / dis_x);
val = (Number(dis_y * perc) + Number(keyData.s)).toFixed(2);
break;
}
複製代碼
經常使用的動畫中除了 opacity(透明)、scale(縮放)、rotation(旋轉)、position(位置)使用到速率的變化,而position(位置)會用到複雜的曲線路徑。曲線路徑也會使用貝塞爾,從position數據中,會發現多出2個屬性。ti 和 to,稱之爲曲線路徑控制點。固然也是一個3階的貝塞爾曲線,只不過這個是用來求路徑點軌跡,而不是求速率。
lottie動畫會利用2個控制點和貝塞爾三階公式,在一段關鍵幀動畫中,默認描繪150個路徑軌跡點來表明這一段的曲線路徑軌跡。同理,本身可使用BezFn.js庫去求出相應的150個曲線路徑點。而後結合以某種速率下屬性值佔比,而後用路徑點集合 * perc (佔比),取整求出當前幀下的當前位置的路徑點,最終實現流暢的曲線路徑動畫。下面是關鍵代碼
// layer.fnc[i] 是用來求當前幀時間佔比下速率所對應的屬性值佔比
perc = layer.fnc[i]( (currentFrame - keyData.t) / dis_x );
perc = perc > 1 ? 1 : perc;
index = Math.round( defaultCurveSegments * perc ) - 1 ;
index = index < 0 ? 0 : index;
// layer.bez[i] 是利用ti、to求出的150個路徑點座標位置集合
val = layer.bez[i][index].point;
複製代碼
利用lottie動畫原理,就算不用lottie庫也能實現流暢的動畫。我本身嘗試寫了一個lottie-light輕量的庫,能完成上文提到的5個屬性值變化的動畫,下面是一些實踐案例。
獅子頭部位
舞臺閃爍
但願本文對你有啓發,能夠把原理應用到各類複雜的動畫中去。但願有多一些想法交流~
更多精彩內容,盡請關注騰訊VTeam技術團隊微信公衆號和視頻號
原做者: 鍾盛照
未經贊成,禁止轉載!