Flutter 中的動畫系統基於型別化的 Animation 物件。Widget 可以透過直接讀取其當前值並監聽其狀態變化,將其動畫直接整合到其構建函式中,或者它們可以使用這些動畫作為基礎,構建更精密的動畫並將其傳遞給其他 widget。

動畫

#

動畫系統的主要構建塊是 Animation 類。動畫表示一個特定型別的值,該值可以在動畫的生命週期內發生變化。大多數執行動畫的 widget 都將一個 Animation 物件作為引數接收,它們從該物件讀取動畫的當前值,並監聽該值的變化。

addListener

#

每當動畫的值發生變化時,動畫都會通知所有透過 addListener 新增的監聽器。通常,監聽動畫的 State 物件會在其監聽器回撥中呼叫其自身的 setState,以通知 widget 系統它需要使用動畫的新值進行重建。

這種模式非常常見,因此有兩個 widget 可以幫助 widget 在動畫值發生變化時重建:AnimatedWidgetAnimatedBuilder。第一個 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 狀態。

Animation­Controller

#

要建立動畫,首先建立 AnimationController。除了自身是一個動畫之外,AnimationController 還允許你控制動畫。例如,你可以告訴控制器 forward 播放動畫或 stop 動畫。你還可以 fling 動畫,它使用物理模擬(例如彈簧)來驅動動畫。

建立動畫控制器後,你就可以開始構建基於它的其他動畫。例如,你可以建立一個 ReverseAnimation,它映象原始動畫但以相反方向執行(從 1.0 到 0.0)。同樣,你可以建立一個 CurvedAnimation,其值由 Curve 調整。

Tweens

#

要將動畫超出 0.0 到 1.0 的範圍,你可以使用 Tween<T>,它在其 beginColorTween 在顏色之間進行插值,而 RectTween 在矩形之間進行插值。你可以透過建立自己的 Tween 子類並覆蓋其 lerp 函式來定義自己的插值。

就其本身而言,補間只是定義如何在兩個值之間進行插值。要獲取動畫當前幀的具體值,你還需要一個動畫來確定當前狀態。有兩種方法可以將補間與動畫結合起來以獲取具體值

  1. 你可以在動畫的當前值處 evaluate 補間。這種方法對於已經監聽動畫並在動畫值發生變化時進行重建的 widget 最有用。

  2. 你可以根據動畫 animate 補間。animate 函式不是返回單個值,而是返回一個包含補間的新 Animation。當你想將新建立的動畫提供給另一個 widget 時,這種方法最有用,該 widget 可以讀取包含補間的當前值以及監聽值的變化。

架構

#

動畫實際上是由許多核心構建塊構建的。

排程器

#

SchedulerBinding 是一個單例類,它暴露了 Flutter 的排程原語。

對於本討論,關鍵原語是幀回撥。每當需要在螢幕上顯示幀時,Flutter 的引擎會觸發一個“開始幀”回撥,排程器將其多路複用到所有使用 scheduleFrameCallback() 註冊的監聽器。所有這些回撥都以 Duration 形式的時間戳,從某個任意紀元開始,接收幀的官方時間戳。由於所有回撥具有相同的時間,因此從這些回撥觸發的任何動畫都將顯得完全同步,即使它們需要幾毫秒才能執行。

Tickers

#

Ticker 類透過排程器的 scheduleFrameCallback() 機制在每個 tick 呼叫回撥。

Ticker 可以啟動和停止。啟動時,它返回一個 Future,該 Future 將在它停止時解析。

在每個 tick,Ticker 會向回撥提供自啟動後第一個 tick 以來的持續時間。

由於 tickers 總是提供相對於它們啟動後第一個 tick 的經過時間;tickers 都是同步的。如果你在兩個 tick 之間以不同的時間啟動三個 tickers,它們仍然會同步到相同的啟動時間,並隨後同步地計時。就像公交車站的人們一樣,所有 tickers 都等待定期發生的事件(tick)開始移動(計時)。

模擬

#

Simulation 抽象類將相對時間值(經過時間)對映到雙精度值,並具有完成的概念。

原則上,模擬是無狀態的,但在實踐中,某些模擬(例如,BouncingScrollSimulationClampingScrollSimulation)在查詢時會不可逆地改變狀態。

各種具體的 Simulation 類實現,用於不同的效果。

Animatables

#

Animatable 抽象類將雙精度值對映到特定型別的值。

Animatable 類是無狀態和不可變的。

Tweens

#

Tween<T> 抽象類將名義上在 0.0-1.0 範圍內的雙精度值對映到型別化的值(例如,Color 或另一個雙精度值)。它是一個 Animatable

它具有輸出型別 (T) 的概念、該型別的 begin 值和 end 值,以及在給定輸入值(名義上在 0.0-1.0 範圍內的雙精度值)下在 begin 和 end 值之間進行插值 (lerp) 的方法。

Tween 類是無狀態和不可變的。

組合 animatables

#

Animatable<double>(父級)傳遞給 Animatablechain() 方法會建立一個新的 Animatable 子類,該子類首先應用父級的對映,然後應用子級的對映。

曲線

#

Curve 抽象類將名義上在 0.0-1.0 範圍內的雙精度值對映到名義上在 0.0-1.0 範圍內的雙精度值。

Curve 類是無狀態和不可變的。

動畫

#

Animation 抽象類提供給定型別的值、動畫方向和動畫狀態的概念,以及一個監聽器介面,用於註冊在值或狀態改變時呼叫的回撥。

一些 Animation 的子類的值永不改變(kAlwaysCompleteAnimationkAlwaysDismissedAnimationAlwaysStoppedAnimation);在這些子類上註冊回撥沒有效果,因為回撥永遠不會被呼叫。

Animation<double> 變體很特殊,因為它可以用來表示名義上在 0.0-1.0 範圍內的雙精度值,這是 CurveTween 類以及一些 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 來賦予自己生命。它可以啟動和停止。在每個 tick 時,它獲取自啟動以來的經過時間,並將其傳遞給 Simulation 以獲得一個值。然後,它報告該值。如果 Simulation 報告在該時間它已結束,則控制器會停止自身。

動畫控制器可以指定動畫的下限和上限,以及持續時間。

在簡單情況下(使用 forward()reverse()),動畫控制器僅在給定持續時間內從下限到上限(或反之,對於反向)進行線性插值。

使用 repeat() 時,動畫控制器在給定持續時間內使用給定邊界之間的線性插值,但不會停止。

使用 animateTo() 時,動畫控制器在給定持續時間內從當前值到給定目標進行線性插值。如果未向方法提供持續時間,則使用控制器的預設持續時間以及控制器下限和上限描述的範圍來確定動畫的速度。

使用 fling() 時,使用 Force 建立一個特定的模擬,然後使用該模擬來驅動控制器。

使用 animateWith() 時,使用給定的模擬來驅動控制器。

這些方法都返回 Ticker 提供的未來,該未來將在控制器下次停止或更改模擬時解析。

將 animatables 附加到動畫

#

Animation<double>(新的父級)傳遞給 Animatableanimate() 方法會建立一個新的 Animation 子類,該子類行為類似於 Animatable,但由給定的父級驅動。