本文經過代碼層面去分析Flutter動畫的實現過程,介紹了Flutter中的Animation庫以及Physics庫。html
本文會從代碼層面去介紹Flutter動畫,所以不會涉及到Flutter動畫的具體使用。git
Flutter的animation庫只依賴兩個庫,Dart庫以及physics庫。animation是採用Dart編寫的,因此依賴Dart庫是很正常的。physics庫是什麼呢?github
Simple one-dimensional physics simulations, such as springs, friction, and gravity, for use in user interface animations.web
physics庫是一個簡單的物理模擬的庫,包含彈簧、阻尼、重力等物理效果。前篇文章介紹過Flutter動畫,Flutter動畫兩個分類中的一個就是基於物理的動畫(Physics-based animation)。因此能夠猜想出animation庫中有一部分代碼,是實現了另外一種動畫--補間動畫(Tween Animation)。spring
經過這種庫的劃分,也能夠大體猜想出,基於物理動畫的庫是後續添加的。這說明了什麼呢?bash
Flutter基於物理的動畫,其實是至關簡單的。目前實現了彈簧、阻尼、重力三種物理效果,整個庫的代碼量也很少。詳細的代碼在下面的部分介紹,在此處,咱們先來講下基於物理的動畫庫的原理。markdown
基於物理的動畫,給咱們的感受會更真實,這是由於其更符合人們平常生活的感官。例如作一個球體下落的動畫,若是是勻速的下落,給人的感受會不夠真實,實際的生活經驗告訴咱們,球體自由下落應該是會有先慢後快的一個過程。若是讓咱們本身去實現這麼一個動畫效果,咱們會怎麼去處理呢?ide
高中物理咱們學習過自由落體相關的概念,其中的位移計算公式:函數
s = 1/2 * g * t * toop
從公式中咱們知道,自由落體的位移跟時間不是線性關係。咱們能夠根據這個公式,來實時的計算出位移來。
若是是摩擦阻尼或者彈簧呢,也都有相關的物理公式,咱們所謂的基於物理的動畫庫,也就是基於此類公式來實現的,本質上仍是補間動畫,只不過過程遵循物理規律比較複雜罷了。
講解這一部分,也考慮過將Flutter的動畫原理先介紹一下。想了想,前一篇文章介紹過這些普世的動畫原理,Flutter只不過是特定平臺的實現,無非是實現手段的不一樣,所以,Flutter動畫原理的解析,放到本文最後的小節部分,在代碼的基礎上去解釋,筆者以爲更加好理解。
animation.dart定義了動畫的四種狀態,以及核心的抽象類Animation。
這個文件中定義了Animation的四種狀態:
Animation類是Flutter動畫中核心的抽象類,它包含動畫的當前值和狀態兩個屬性。定義了動畫的一系列回調,
void addListener(VoidCallback listener);
void removeListener(VoidCallback listener);
複製代碼
void addStatusListener(AnimationStatusListener listener);
void removeStatusListener(AnimationStatusListener listener);
複製代碼
A curve must map t=0.0 to 0.0 and t=1.0 to 1.0.
看到這段英文,首先會想到什麼?沒錯,插值器。Curve也是一個抽象類,定義了時間與數值的一個接口。
double transform(double t);
複製代碼
例如一個線性的插值器,實現代碼以下。
class _Linear extends Curve {
const _Linear._();
@override
double transform(double t) => t;
}
複製代碼
該文件下面定義了很是多類型的插值器,具體的實現不一一展開了。Flutter定義了一系列的插值器,封裝在Curves類中,有下面13種效果。
若是上面的13種還不知足需求的話,還可使用Cubic類來進行自定義的構造。能夠看出這塊兒實現參考了web中的相關實現。
該文件定義了一系列的估值器,Flutter經過抽象類Animatable來實現估值器。關於Animatable,咱們能夠先看下其定義。
An object that can produce a value of type
T
given an [Animation] as input.
能夠根據不一樣的輸入,產出不一樣的數值。經過重載下面的函數來產生不一樣的估值器。
T transform(double t);
複製代碼
它的最主要的子類是Tween,一個線性的估值器,實現以下,很是的簡單,就是一個線性函數。
T lerp(double t) { assert(begin != null); assert(end != null); return begin + (end - begin) * t; } @override T transform(double t) { if (t == 0.0) return begin; if (t == 1.0) return end; return lerp(t); } 複製代碼
在Tween的基礎上實現了不一樣類型的估值器。
還能夠經過自定義的插值器去實現估值器,例如經過curve實現的估值器CurveTween。
動畫的控制,就在這個文件下面實現,其中最重要的部分是AnimationController,它派生自Animation類。
AnimationController的功能有以下幾種:
默認狀況下,AnimationController是線性的產生0.0到1.0之間的值,每刷新一幀就產出一個數值。AnimationController在不使用的時候須要dispose,不然會形成資源的泄漏。
提到AnimationController必需要先說一下TickerProvider。
An interface implemented by classes that can vend Ticker objects.
TickerProvider定義了能夠發送Ticker對象的接口,
Ticker createTicker(TickerCallback onTick);
複製代碼
Ticker能幹什麼呢?
Tickers can be used by any object that wants to be notified whenever a frame triggers.
它的主要做用是獲取每一幀刷新的通知,做用就顯而易見了,至關於給動畫添加了一個動起來的引擎。
如今再次回到AnimationController。上面爲何要先說一下TickerProvider呢,這是由於AnimationController的構造函數中須要一個TickerProvider參數。
結合上面介紹的插值器、估值器以及Ticker回調,AnimationController大體的工做流程,我相信不少人均可以理出來了。
隨着時間的流逝,插值器根據時間產生的值做爲輸入,提供給估值器,產生動畫的實際效果值,結合Ticker的回調,渲染出當前動畫值的圖像。這也是補間動畫的工做原理。
AnimationController具體的源碼不作分析了,能夠看到Flutter的動畫實現的實際上是至關的原始,AnimationController須要一個觸發刷新的回調,輸出也是值的改變,並不像成熟平臺裏面的配合View去作動畫。
Physics庫基本上就是插值器的實現部分,這部分比較簡單
Simulation定義了基於物理動畫的相關接口,具體有位置、速度、是否完成以及公差(Tolerance)
double x(double time);
double dx(double time);
複製代碼
GravitySimulation的實現以下,其中_a加速度,_x是初始距離,_v是初始速度:
@override
double x(double time) => _x + _v * time + 0.5 * _a * time * time;
@override
double dx(double time) => _v + time * _a;
複製代碼
相信學太高中物理的讀者,對這公式不會陌生。其餘幾種具體實現不在此處一一展開了哈。若是擴展這個物理動畫庫的話,也很好去擴展,掌握一些物理公式,就能夠去仿照實現了。
關於動畫的驅動,在此簡單的說一下,Ticker是被SchedulerBinding所驅動。SchedulerBinding則是監聽着Window.onBeginFrame回調。
Window.onBeginFrame的做用是什麼呢,是告訴應用該提供一個scene了,它是被硬件的VSync信號所驅動的。
具體能夠查看sky_engine下面的window.dart的實現,不作展開了。
本篇文章簡單的從代碼的層面解析了一下Flutter的動畫,更深刻的Ticker這塊兒,感興趣的讀者能夠自行去了解,這塊兒涉及到sky_engine下面的代碼。
本篇文章,筆者依然試圖繞過代碼去講解一些普適性的東西,可是Flutter這塊兒代碼實現的確實挺簡單的,形成的問題是調用起來費勁。
基於物理的動畫,咱們要知道深層次的是物理公式,有這個基礎,咱們才能夠製做出符合感官的動畫效果。其本質也是補間動畫,過程能夠被計算出來。
能夠說的寬泛一些,通常的動畫,大部分都是補間動畫,若是咱們自行去設計一套動畫系統,插值器、估值器、驅動部分以及動畫的管理部分,這四個模塊之間相互協調輸出一幀一幀的動畫過場。絕大部分平臺的動畫設計,也都逃不過這些因素,只不過實現的方式各不相同。
若是文中有錯誤的地方,煩請指正,筆者水平有限,再次感謝。
筆者建了一個Flutter學習相關的項目,Github地址,裏面包含了筆者寫的關於Flutter學習相關的一些文章,會按期更新,也會上傳一些學習Demo,歡迎你們關注。