什么是動(dòng)畫(huà)?
-
人類具有“視覺(jué)暫留”的特性,人的眼睛看到畫(huà)面在0.34秒內(nèi)不會(huì)消失。利用這一原理,在一幅畫(huà)還沒(méi)有消失前播放下一幅畫(huà),就會(huì)給人造成一種流暢的視覺(jué)變化效果。因此,當(dāng)一段時(shí)間內(nèi),連續(xù)的對(duì)UI進(jìn)行改變;人的眼睛就會(huì)產(chǎn)生視覺(jué)暫留,最終看到的就是一個(gè)“連續(xù)”的動(dòng)畫(huà);對(duì)于人眼來(lái)說(shuō),一般幀率只要超過(guò)16FPS,就會(huì)比較流暢,超過(guò)32FPS就會(huì)較為細(xì)膩平滑,超過(guò)32FPS基本感受不到差別了。
image
Flutter 動(dòng)畫(huà)簡(jiǎn)述
- 由于 Flutter 動(dòng)畫(huà)中, 每一幀的變化都是需要對(duì)UI進(jìn)行改變,短時(shí)間內(nèi)連續(xù)的改變UI輸出會(huì)非常的消耗資源的,因此在UI系統(tǒng)中,動(dòng)畫(huà)的平均幀率是重要的性能指標(biāo);Flutter 動(dòng)畫(huà)在理想狀態(tài)下是可以實(shí)現(xiàn)60FPS的,這和原生應(yīng)用能達(dá)到的幀率是基本是持平;Flutter 中對(duì)動(dòng)畫(huà)進(jìn)行了抽象,主要通過(guò)
Animation、Curve、Controller、Tween
配合來(lái)完成動(dòng)畫(huà)-
Animation
對(duì)象是Flutter動(dòng)畫(huà)庫(kù)中的一個(gè)核心類,它生成指導(dǎo)動(dòng)畫(huà)的值。 -
Animation
對(duì)象知道動(dòng)畫(huà)的當(dāng)前狀態(tài)(例如,它是開(kāi)始、停止還是向前或向后移動(dòng)),但它不知道屏幕上顯示的內(nèi)容。 -
AnimationController
管理Animation
。 -
CurvedAnimation
將過(guò)程抽象為一個(gè)非線性曲線. -
Tween
在正在執(zhí)行動(dòng)畫(huà)的對(duì)象所使用的數(shù)據(jù)范圍之間生成值。例如,Tween
可能會(huì)生成從紅到藍(lán)之間的色值,或者從 0 到 255。 - 使用
Listeners
和StatusListeners
監(jiān)聽(tīng)動(dòng)畫(huà)狀態(tài)改變。
-
Animation
Flutter中的動(dòng)畫(huà)系統(tǒng)基于 Animation
對(duì)象的,widget 可以在 build
函數(shù)中讀取 Animation
對(duì)象的當(dāng)前值, 并且可以監(jiān)聽(tīng)動(dòng)畫(huà)的狀態(tài)改變。
Animation
對(duì)象本身和 UI 渲染沒(méi)有任何關(guān)系。Animation
是一個(gè)抽象類,它擁有其當(dāng)前值和狀態(tài)(完成或停止)。其中一個(gè)比較常用的Animation
類是Animation<double>
。Animation
對(duì)象是一個(gè)在一段時(shí)間內(nèi)依次生成一個(gè)區(qū)間之間值的類, 對(duì)象的輸出可以是線性的、曲線的、一個(gè)步進(jìn)函數(shù)或者任何其他可以設(shè)計(jì)的映射, 根據(jù)Animation
對(duì)象的控制方式,動(dòng)畫(huà)可以反向運(yùn)行,甚至可以在中間切換方向。Animation
還可以生成除double
之外的其他類型值,如:Animation<Color>
或Animation<Size>
;Animation
對(duì)象有狀態(tài),在動(dòng)畫(huà)的每一幀中,我們可以通過(guò)Animation
對(duì)象的value
屬性獲取動(dòng)畫(huà)的當(dāng)前狀態(tài)值。-
動(dòng)畫(huà)監(jiān)聽(tīng)
- 使用
addListener()
給Animation
添加幀監(jiān)聽(tīng)器,在每一幀都會(huì)被調(diào)用。最常見(jiàn)的操作是改變狀態(tài)后調(diào)用setState()
來(lái)觸發(fā)UI重建。 - 使用
addStatusListener()
給Animation
添加 “動(dòng)畫(huà)狀態(tài)改變” 監(jiān)聽(tīng)器;動(dòng)畫(huà)開(kāi)始、結(jié)束、正向或反向(見(jiàn)AnimationStatus
定義)時(shí)會(huì)調(diào)用狀態(tài)改變的監(jiān)聽(tīng)器。
- 使用
CurvedAnimation
-
CurvedAnimation
將動(dòng)畫(huà)過(guò)程定義為一個(gè)非線性曲線 - Flutter 中通過(guò)
Curve
(曲線)來(lái)描述動(dòng)畫(huà)過(guò)程,我們把勻速動(dòng)畫(huà)稱為線性的(Curves.linear
),而非勻速動(dòng)畫(huà)稱為非線性的
final CurvedAnimation curve =
new CurvedAnimation(parent: controller, curve: Curves.easeIn);
-
Curves
類定義了許多常用的曲線,我們也可以選擇自定義
class ShakeCurve extends Curve {
@override
double transform(double t) {
return math.sin(t * math.PI * 2);
}
}
-
CurvedAnimation
和AnimationController
都是Animation<double>
類型,CurvedAnimation
可以通過(guò)包裝AnimationController
和Curve
生成一個(gè)新的動(dòng)畫(huà)對(duì)象 ,通過(guò)這種方式來(lái)將動(dòng)畫(huà)和動(dòng)畫(huà)執(zhí)行的曲線進(jìn)行關(guān)聯(lián);
AnimationController
-
AnimationController
是一個(gè)特殊的Animation
對(duì)象,在屏幕刷新的每一幀,就會(huì)生成一個(gè)新的值。它包含動(dòng)畫(huà)的啟動(dòng)forward()、stop()、reverse()
等方法;默認(rèn)情況下,AnimationController
在給定的時(shí)間段內(nèi)會(huì)線性的生成0.0~1.0
的數(shù)字,另外在創(chuàng)建AnimationController
時(shí),需要傳遞一個(gè)vsync
參數(shù),存在vsync
時(shí)會(huì)防止動(dòng)畫(huà)的UI不在當(dāng)前屏幕時(shí)消耗不必要的資源 。 例如,下面代碼創(chuàng)建一個(gè)Animation對(duì)象,但不會(huì)啟動(dòng)它運(yùn)行
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
注:vsync 對(duì)象會(huì)綁定動(dòng)畫(huà)的定時(shí)器到一個(gè)可視的 widget,當(dāng) widget 不顯示時(shí),動(dòng)畫(huà)定時(shí)器將會(huì)暫停,當(dāng) widget 再次顯示時(shí),動(dòng)畫(huà)定時(shí)器重新恢復(fù)執(zhí)行,這樣就可以避免動(dòng)畫(huà)相關(guān)UI不在當(dāng)前屏幕時(shí)消耗資源。 如果要使用自定義的State 對(duì)象作為 vsync 時(shí),請(qǐng)包含 TickerProviderStateMixin;
注意: 在某些情況下,值(position,指動(dòng)畫(huà)的當(dāng)前值)可能會(huì)超出AnimationController 的 0.0-1.0 的范圍。例如,fling() 函數(shù)允許您提供速度(velocity)、力量(force)、position(通過(guò)Force對(duì)象)。位置(position)可以是任何東西,因此可以在0.0到1.0范圍之外。 CurvedAnimation 生成的值也可以超出0.0到1.0的范圍。根據(jù)選擇的曲線,CurvedAnimation 的輸出可以具有比輸入更大的范圍。例如,Curves.elasticIn 等彈性曲線會(huì)生成大于或小于默認(rèn)范圍的值。
-
AnimationController
生成數(shù)字的區(qū)間可以通過(guò)lowerBound
和upperBound
來(lái)指定
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 2000),
lowerBound: 10.0,
upperBound: 20.0,
vsync: this
);
-
AnimationController
派生自Animation<double>
,因此可以在需要Animation
對(duì)象的任何地方使用。 但是,AnimationController
具有控制動(dòng)畫(huà)的其他方法。例如,.forward()
方法可以啟動(dòng)動(dòng)畫(huà)。數(shù)字的產(chǎn)生與屏幕刷新有關(guān),因此每秒鐘通常會(huì)產(chǎn)生60個(gè)數(shù)字,在生成每個(gè)數(shù)字后,每個(gè)Animation
對(duì)象調(diào)用添加的Listener
對(duì)象。 - 當(dāng)動(dòng)畫(huà)開(kāi)始執(zhí)行后會(huì)開(kāi)始生成動(dòng)畫(huà)幀,屏幕每刷新一次就是一個(gè)動(dòng)畫(huà)幀,每一個(gè)動(dòng)畫(huà)幀,會(huì)隨著動(dòng)畫(huà)的曲線來(lái)生成當(dāng)前的
Animation.value
,然后根據(jù)當(dāng)前的Animation.value
去構(gòu)建UI,當(dāng)所有動(dòng)畫(huà)幀依次觸發(fā)時(shí),Animation.value
會(huì)依次改變,構(gòu)建的UI也會(huì)跟隨著依次變化,最終我們可以看到一個(gè)完成的動(dòng)畫(huà)。 另外在動(dòng)畫(huà)的每一幀,Animation
對(duì)象會(huì)調(diào)用其幀監(jiān)聽(tīng)器,等動(dòng)畫(huà)狀態(tài)發(fā)生改變時(shí)會(huì)調(diào)用狀態(tài)改變監(jiān)聽(tīng)器。
Tween
- 默認(rèn)情況下,
AnimationController
對(duì)象的范圍為0.0~1.0
。如果您需要不同的范圍或不同的數(shù)據(jù)類型,則可以使用Tween
來(lái)配置動(dòng)畫(huà)以生成不同的范圍或數(shù)據(jù)類型的值;Tween
是一個(gè)無(wú)狀態(tài)(stateless
)對(duì)象,需要begin
和end
值。Tween
的唯一職責(zé)就是定義從輸入范圍到輸出范圍的映射。輸入范圍通常為0.0~1.0
,但這不是必須的;
final Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);
-
Tween
繼承自Animatable<T>
,而非Animation<T>
。Animatable
與Animation
相似,不是必須輸出double
值。例如,ColorTween
指定兩種顏色之間的過(guò)渡
final Tween colorTween =
new ColorTween(begin: Colors.transparent, end: Colors.black54);
-
Tween
對(duì)象不存儲(chǔ)任何狀態(tài),它提供了evaluate(Animation<double> animation)
方法將映射函數(shù)應(yīng)用于動(dòng)畫(huà)當(dāng)前值。Animation
對(duì)象的當(dāng)前值可以通過(guò)value()
方法取到。evaluate
函數(shù)還執(zhí)行一些其它處理,例如分別確保在動(dòng)畫(huà)值為0.0~1.0
時(shí)返回開(kāi)始和結(jié)束狀態(tài) - 使用
Tween
對(duì)象,需要調(diào)用其animate()
方法,傳入一個(gè)控制器對(duì)象。
// 需要注意的是 `animate()` 返回的是一個(gè) `Animation`,而不是一個(gè) `Animatable`
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(controller);
- 以下示例構(gòu)建了一個(gè)控制器、一條曲線和一個(gè)
Tween
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
final Animation curve =
new CurvedAnimation(parent: controller, curve: Curves.easeOut);
Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(curve);