動畫概覽
Flutter 中的動畫系統基於 Animation
。
Widgets 可以直接將這些動畫合併到自己的 build 方法中來讀取它們的當前值或者監聽它們的狀態變化,或者可以將其作為的更復雜動畫的基礎傳遞給其他 widgets。
Animation
動畫系統的首要組成部分就是 Animation
類別。一個動畫表現為可在它的生命週期內發生變化的特定型別的值。大多數需要執行動畫的 widgets 都需要接收一個
Animation
物件作為引數,從而能從中獲取到動畫的當前狀態值以及應該監聽哪些具體值的更改。
addListener
每當動畫的狀態值發生變化時,動畫都會通知所有透過 addListener
新增的監聽器。通常,一個正在監聽動畫的 State
物件會呼叫自身的 setState
方法,將自身傳入這些監聽器的回呼(Callback)函式來通知
widget 系統需要根據新狀態值進行重新建構。
這種模式非常常見,所以有兩個 widgets
可以幫助其他 widgets 在動畫改變值時進行重新建構:
AnimatedWidget
和 AnimatedBuilder
。第一個是 AnimatedWidget
,對於無狀態動畫 widgets 來說是尤其有用的。要使用 AnimatedWidget
,只需繼承它並實現一個 build
方法。第二個是 AnimatedBuilder
,對於希望將動畫作為複雜 widgets 的
build 方法的其中一部分的情況非常有用。要使用 AnimatedBuilder
,只需構造 widget 並將 AnimatedBuilder
傳遞給 widget 的 builder
方法。
addStatusListener
動畫還提供了一個 AnimationStatus
,表示動畫將如何隨時間進行變化。每當動畫的狀態發生變化時,動畫都會通知所有透過 addStatusListener
新增的監聽器。通常情況下,動畫會從 dismissed
狀態開始,表示它處於變化區間的開始點。舉例來說,從 0.0 到 1.0 的動畫在 dismissed
狀態時的值應該是 0.0。動畫進行的下一狀態可能是 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
來調整動畫的值。
補間動畫
如果想要在 0.0 到 1.0 的區間之外設定動畫,可以使用 Tween<T>
,它可以在它的 begin
值和
end
值之間進行插值補間。許多類都有特定的 Tween
子類別,它們能提供基於特定型別的插值行為。例如, ColorTween
可以在顏色間進行插值,
RectTween
可以在矩形之間進行插值。你可以透過建立自己的 Tween
子類別並覆蓋其
lerp
方法來定義自己的補間動畫。
補間動畫本身只定義瞭如何在兩個值之間進行插值。要獲取動畫當前幀的具體值,還需要一個動畫來確定當前狀態。有兩種方法可以將補間動畫與動畫組合在一起以獲得動畫的具體值:
-
你可以用
evaluate
方法處理動畫的當前值從而得到對應的插值。這種方法對於已經監聽動畫並因此在動畫改變值時重新建構的 widgets 是最有效的。 -
你可以用
animate
方法處理一個動畫。相對於返回單個值,animate 方法返回一個包含補間動畫插值的新的Animation
。這種方法對於當你想要將新建立的動畫提供給另一個 widget 時最有效,它可以直接讀取包含補間動畫的插值以及監聽對應插值的更改。
架構
動畫實際上是由許多核心模組共同建構的。
排程器
SchedulerBinding
是一個暴露出 Flutter 排程原語的單例類別。
在這一節,關鍵原語是幀回呼(Callback)。每當一幀需要在螢幕上顯示時,
Flutter 的引擎會觸發一個 “開始幀” 回呼(Callback),排程程式會將其多路傳輸給所有使用
scheduleFrameCallback()
註冊的監聽器。所有這些回呼(Callback)不管在任意狀態或任意時刻都可以收到這一幀的絕對時間戳。由於所有回呼(Callback)收到時間戳都相同,因此這些回呼(Callback)觸發的任何動畫看起來都是完全同步的,即使它們需要幾毫秒才能執行。
執行器
Ticker
類掛載在排程器的 scheduleFrameCallback()
的機制上,來達到每次執行都會觸發回呼(Callback)的效果。
一個 Ticker
可以被啟動和停止,啟動時,它會返回一個 Future
,這個 Future
在 Ticker
停止時會被改為完成狀態。
每次執行, Ticker
都會為回呼(Callback)函式提供從
Ticker
開始執行到現在的持續時間。
因為執行器總是會提供在自它們開始執行以來的持續時間,所以所有執行器都是同步的。如果你在兩幀之間的不同時刻啟動三個執行器,它們都會被同步到相同的開始時間,並隨後同步執行。
模擬器
Simulation
抽象類別將相對時間值(執行時間)對映為雙精度值,並且有完成的概念。
原則上,模擬器是無狀態的,但在實踐中,一些模擬器(例如 BouncingScrollSimulation
和 ClampingScrollSimulation
)在查詢時會不可逆地被改變狀態。
針對不同的效果,Simulation
類有
各種具體實現。
Animatables
Animatable
抽象類別將雙精度值對映為特定型別的值。
Animatable
類是無狀態和不可變的。
補間動畫
Tween<T>
抽象類別將名義範圍為 0.0-1.0
的雙精度值對映到某個型別值(例如 Color
或其他雙精度值)。它屬於 Animatable
。
它有一個輸出型別(T
)的概念,這個輸出型別有一個 begin
值和一個end
值,以及在給定輸入值的起始值和結束值(名義範圍為 0.0-1.0 的雙精度值)之間插值(lerp
)的方法。
Tween
類是無狀態和不可變的。
組合 animatables
將 Animatable<double>
(父類)傳遞給一個
Animatable
的 chain()
方法會建立一個新的
Animatable
子類別,這個子類別會先應用父類別的對映,然後應用子類別的對映。
曲線
Curve
抽象類別將名義範圍為 0.0-1.0 的雙精度值對映到名義範圍為 0.0-1.0 的雙精度值。
Curve
類是無狀態和不可變的。
動畫
Animation
抽象類別提供給定型別的值、動畫方向的概念和動畫狀態和一個監聽器介面,這個監聽器介面用來註冊值或狀態的改變時被呼叫的回呼(Callback)。
有些 Animation
的子類別值是永遠不變的(kAlwaysCompleteAnimation
、kAlwaysDismissedAnimation
和
AlwaysStoppedAnimation
),在這些子類別上註冊回呼(Callback)沒有任何效果,因為這些回呼(Callback)永遠不會被呼叫。
Animation<double>
變數很特殊,因為它可以被用來表示名義範圍為 0.0-1.0 的雙精度值,也就是 Curve
和 Tween
類以及動畫的一些其他子類別所期望的輸入。
有些 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()
、play()
或者 resume()
),動畫控制器只是簡單地在持續時間內線性地從下限至上限(反之亦然,用於在反向方向)進行插值補間。
當使用 repeat()
時,動畫控制器會在持續時間內線性地在上下邊界之間進行插值補間,但會一直重複,不會停止。
當使用 animateTo()
時,動畫控制器會在持續時間內線性地從當前值到給定目標值進行插值補間。如果方法沒有指定持續時間,則使用控制器的預設持續時間和控制器的上下限範圍來確定動畫的速度。
當使用 fling()
時,一個 Force
被用來建立一個特定的模擬器,然後用來驅動控制器。
當使用 animateWith()
時,給定的模擬器會被用於驅動控制器。
這些方法都會返回 Ticker
提供的將來值,交由控制器下一次停止或改變模擬器時來完成。
將 animatables 附加到動畫上
將 Animation<double>
(新父級)傳遞給一個
Animatable
類別的 animate()
方法將建立一個新的 Animation
子類別,它的作用類似於 Animatable
,但是由給定的父級驅動。