動畫 API 概覽
動畫概念概覽。
Flutter 中的動畫系統基於型別化的 Animation 物件。Widget 既可以在其構建函式中透過讀取當前值並監聽狀態變化來直接整合這些動畫,也可以將這些動畫作為更復雜動畫的基礎,傳遞給其他 Widget。
動畫
#動畫系統的主要構建塊是 Animation 類。動畫表示一個特定型別的值,該值可以在動畫的生命週期內發生變化。大多數執行動畫的 Widget 都會接收一個 Animation 物件作為引數,從中讀取動畫的當前值,並監聽該值的變化。
addListener
#
每當動畫的值發生變化時,動畫會通知所有透過 addListener 新增的監聽器。通常,監聽動畫的 State 物件會在其監聽器回撥中呼叫自身的 setState,以通知 Widget 系統需要使用新的動畫值進行重建。
這種模式非常普遍,因此有兩個 Widget 可以幫助在動畫值改變時重建 Widget:AnimatedWidget 和 AnimatedBuilder。前者 AnimatedWidget 最適用於無狀態的動畫 Widget。要使用 AnimatedWidget,只需將其子類化並實現 build 函式即可。後者 AnimatedBuilder 則適用於希望將動畫作為大型構建函式一部分的更復雜的 Widget。要使用 AnimatedBuilder,只需構造該 Widget 並傳遞一個 builder 函式即可。
addStatusListener
#
動畫還提供了一個 AnimationStatus,用於指示動畫隨時間演變的狀態。每當動畫的狀態發生變化時,動畫會通知所有透過 addStatusListener 新增的監聽器。通常,動畫從 dismissed 狀態開始,這意味著它們處於範圍的起點。例如,從 0.0 到 1.0 的動畫,當其值為 0.0 時即為 dismissed。隨後,動畫可能會以 forward(從 0.0 到 1.0)或 reverse(從 1.0 到 0.0)方式執行。最終,如果動畫到達其範圍的終點 (1.0),動畫將達到 completed 狀態。
AnimationController
#要建立動畫,首先需要建立一個 AnimationController。AnimationController 不僅本身是一個動畫,還可以讓您控制動畫。例如,您可以告訴控制器 forward(向前)播放動畫或 stop(停止)動畫。您還可以 fling(拋擲)動畫,它使用物理模擬(如彈簧)來驅動動畫。
建立動畫控制器後,您可以基於它構建其他動畫。例如,您可以建立一個 ReverseAnimation,它映象原始動畫但以相反的方向執行(從 1.0 到 0.0)。同樣,您可以建立一個 CurvedAnimation,其值由 Curve 進行調整。
Tweens
#要實現 0.0 到 1.0 區間之外的動畫,可以使用 Tween<T>,它在 begin 和 end 值之間進行插值。許多型別都有特定的 Tween 子類,提供型別特定的插值。例如,ColorTween 在顏色之間進行插值,RectTween 在矩形之間進行插值。您可以透過建立自己的 Tween 子類並覆蓋其 lerp 函式來定義自己的插值。
Tween 本身只定義瞭如何在兩個值之間進行插值。為了獲得動畫當前幀的具體值,還需要一個動畫來確定當前狀態。有兩種方法可以將 Tween 與動畫結合以獲得具體值:
-
您可以在動畫的當前值處
evaluateTween。這種方法最適用於已經監聽動畫並因此在動畫值變化時進行重建的 Widget。 -
您可以基於動畫來
animateTween。animate函式不會返回單個值,而是返回一個包含該 Tween 的新Animation。當您希望將新建立的動畫提供給另一個 Widget 時,這種方法最為有用,該 Widget 可以讀取包含該 Tween 的當前值,並監聽值的變化。
架構
#動畫實際上是由多個核心構建塊組成的。
Scheduler
#SchedulerBinding 是一個單例類,它暴露了 Flutter 的排程原語。
在此討論中,關鍵的原語是幀回撥。每當需要在螢幕上顯示一幀時,Flutter 引擎就會觸發一個“begin frame”回撥,排程程式將其多路複用到所有使用 scheduleFrameCallback() 註冊的監聽器上。所有這些回撥都會獲得該幀的正式時間戳,以相對於某個任意紀元的 Duration 形式表示。由於所有回撥具有相同的時間,因此從這些回撥觸發的任何動畫即使在執行時耗費了幾毫秒,看起來也將完全同步。
Tickers
#Ticker 類掛接到排程程式的 scheduleFrameCallback() 機制中,以便在每次滴答時呼叫回撥。
Ticker 可以啟動和停止。啟動時,它會返回一個 Future,當它停止時該 Future 將解析。
在每次滴答時,Ticker 會向回撥提供自啟動後第一次滴答以來的持續時間。
因為 Ticker 總是提供相對於啟動後第一次滴答的流逝時間,所以所有的 Ticker 都是同步的。如果您在兩次滴答之間的不同時間啟動三個 Ticker,它們仍然會與相同的開始時間同步,並隨後同步滴答。就像公交車站的人一樣,所有的 Ticker 都會等待一個定期發生的事件(滴答)來開始移動(計算時間)。
Simulations
#Simulation 抽象類將相對時間值(流逝時間)對映到一個 double 值,並具有完成的概念。
原則上,模擬是無狀態的,但在實踐中,某些模擬(例如 BouncingScrollSimulation 和 ClampingScrollSimulation)在查詢時會發生不可逆的狀態改變。
對於不同的效果,Simulation 類有 各種具體實現。
Animatables
#Animatable 抽象類將 double 對映為特定型別的值。
Animatable 類是無狀態且不可變的。
Tweens
#Tween<T> 抽象類將名義上在 0.0-1.0 範圍內的 double 值對映為型別化的值(例如 Color 或另一個 double)。它是一個 Animatable。
它具有輸出型別 (T) 的概念,以及該型別的 begin 值和 end 值,還有一種為給定輸入值(名義上在 0.0-1.0 範圍內的 double)在開始值和結束值之間進行插值 (lerp) 的方法。
Tween 類是無狀態且不可變的。
組合 Animatable
#將 Animatable<double>(父級)傳遞給 Animatable 的 chain() 方法,會建立一個新的 Animatable 子類,該子類先應用父級的對映,然後再應用子級的對映。
Curves
#Curve 抽象類將名義上在 0.0-1.0 範圍內的 double 值對映為名義上在 0.0-1.0 範圍內的 double 值。
Curve 類是無狀態且不可變的。
動畫
#Animation 抽象類提供給定型別的值、動畫方向和動畫狀態的概念,以及用於註冊在值或狀態改變時被呼叫的回撥的監聽器介面。
某些 Animation 子類的值永遠不會改變(kAlwaysCompleteAnimation、kAlwaysDismissedAnimation、AlwaysStoppedAnimation);在這些物件上註冊回撥沒有效果,因為回撥永遠不會被呼叫。
Animation<double> 變體比較特殊,因為它可以用來表示名義上在 0.0-1.0 範圍內的 double,這是 Curve 和 Tween 類以及某些 Animation 子類所期望的輸入。
某些 Animation 子類是無狀態的,僅將監聽器轉發給其父級。有些則是有狀態的。
可組合動畫
#大多數 Animation 子類都採用明確的“父級” Animation<double>。它們由該父級驅動。
CurvedAnimation 子類採用 Animation<double> 類(父級)和幾個 Curve 類(向前和向後曲線)作為輸入,並使用父級的值作為曲線的輸入來確定其輸出。CurvedAnimation 是不可變且無狀態的。
ReverseAnimation 子類採用 Animation<double> 類作為其父級,並反轉動畫的所有值。它假設父級使用的值名義上在 0.0-1.0 範圍內,並返回 1.0-0.0 範圍內的值。父動畫的狀態和方向也會被反轉。ReverseAnimation 是不可變且無狀態的。
ProxyAnimation 子類採用 Animation<double> 類作為其父級,並僅轉發該父級的當前狀態。但是,父級是可變的。
TrainHoppingAnimation 子類有兩個父級,並在它們的值發生交叉時在兩者之間切換。
動畫控制器
#AnimationController 是一個有狀態的 Animation<double>,它使用 Ticker 為自身提供生命力。它可以啟動和停止。在每次滴答時,它會將自啟動以來的流逝時間傳遞給 Simulation 以獲得一個值。這就是它報告的值。如果 Simulation 報告此時已結束,則控制器會自動停止。
可以為動畫控制器設定一個下限和上限以便在其中進行動畫處理,以及一個持續時間。
在簡單情況下(使用 forward() 或 reverse()),動畫控制器僅在給定的持續時間內從下限到上限(或反之,對於反向)進行線性插值。
使用 repeat() 時,動畫控制器在給定的持續時間內在給定的邊界之間進行線性插值,但不會停止。
使用 animateTo() 時,動畫控制器在給定的持續時間內從當前值線性插值到給定的目標值。如果未向方法提供持續時間,則使用控制器的預設持續時間以及由控制器下限和上限描述的範圍來確定動畫的速度。
使用 fling() 時,會使用 Force 建立特定的模擬,然後將其用於驅動控制器。
使用 animateWith() 時,給定的模擬將用於驅動控制器。
這些方法都會返回 Ticker 提供的 future,當控制器下次停止或更改模擬時,該 future 將解析。
將 Animatable 附加到動畫
#將 Animation<double>(新父級)傳遞給 Animatable 的 animate() 方法,會建立一個新的 Animation 子類,其行為類似於 Animatable,但由給定的父級驅動。