flutter動畫簡介
在任何系統(tǒng)的UI框架中恶迈,動畫實(shí)現(xiàn)的原理都是相同的涩金,即:在一段時間內(nèi)谱醇,快速地多次改變UI外觀;由于人眼會產(chǎn)生視覺暫留步做,所以最終看到的就是一個“連續(xù)”的動畫副渴。我們將UI的一次改變稱為一個動畫幀,對應(yīng)一次屏幕刷新全度,而決定動畫流暢度的一個重要指標(biāo)就是幀率FPS(Frame Per Second)煮剧,即每秒的動畫幀數(shù)。在UI系統(tǒng)中将鸵,動畫的平均幀率是重要的性能指標(biāo)勉盅,而在Flutter中,理想情況下是可以實(shí)現(xiàn) 60FPS 的顶掉,這和原生應(yīng)用能達(dá)到的幀率是基本是持平的草娜。
flutter中動畫抽象
Flutter中也對動畫進(jìn)行了抽象,主要涉及 Animation痒筒、Curve宰闰、Controller、Tween這四個角色簿透,它們一起配合來完成一個完整動畫移袍,下面我們一一來介紹它們。
Animation
- Animation 是一個抽象類老充,它本身和UI渲染沒有任何關(guān)系葡盗,而它主要的功能是保存動畫的插值和狀態(tài)
- Animation對象是一個在一段時間內(nèi)依次生成一個區(qū)間(Tween)之間值的類
- 在動畫的每一幀中,我們可以通過Animation對象的value屬性獲取動畫的當(dāng)前狀態(tài)值啡浊。
動畫通知
我們可以通過Animation來監(jiān)聽動畫每一幀以及執(zhí)行狀態(tài)的變化觅够。
1.addListener();它可以用于給Animation添加幀監(jiān)聽器巷嚣,在每一幀都會被調(diào)用蔚约。animation.value獲取當(dāng)前動畫值。
2.addStatusListener()涂籽;它可以給Animation添加“動畫狀態(tài)改變”監(jiān)聽器苹祟;動畫開始、結(jié)束评雌、正向或反向(見AnimationStatus定義)時會調(diào)用狀態(tài)改變的監(jiān)聽器树枫。
AnimationController
- AnimationController用于控制動畫,它包含動畫的啟動forward()景东、停止stop() 砂轻、反向播放 reverse()等方法。
- AnimationController會在每一個動畫幀斤吐,就會生成一個新的值搔涝。
- AnimationController派生自Animation<double>厨喂,因此可以在需要Animation對象的任何地方使用。
- 在動畫開始執(zhí)行后開始生成動畫幀庄呈,屏幕每刷新一次就是一個動畫幀蜕煌,在動畫的每一幀,會隨著根據(jù)動畫的曲線來生成當(dāng)前的動畫值诬留,然后根據(jù)當(dāng)前的動畫值去構(gòu)建UI斜纪,當(dāng)所有動畫幀依次觸發(fā)時,動畫值會依次改變文兑,所以構(gòu)建的UI也會依次變化盒刚,所以最終我們可以看到一個完成的動畫都办。
// AnimationController在給定的時間段內(nèi)線性的生成從 0.0 到1.0(默認(rèn)區(qū)間)的數(shù)字
// 下面代碼創(chuàng)建一個Animation對象(但不會啟動動畫)
final AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
);
我們知道屏幕每刷新一次就會隨著根據(jù)動畫的曲線來生成當(dāng)前的動畫值曾棕,那屏幕刷新和動畫之間是通過什么聯(lián)系起來的呢,這里不得不提到Ticker擎宝。
Ticker
當(dāng)創(chuàng)建一個AnimationController時籍铁,需要傳遞一個vsync參數(shù)贮聂,它接收一個TickerProvider類型的對象,它的主要職責(zé)是創(chuàng)建Ticker寨辩。
Flutter 應(yīng)用在啟動時都會綁定一個SchedulerBinding,通過SchedulerBinding可以給每一次屏幕刷新添加回調(diào)歼冰,而Ticker就是通過SchedulerBinding來添加屏幕刷新回調(diào)靡狞,這樣一來,每次屏幕刷新都會調(diào)用TickerCallback隔嫡。使用Ticker(而不是Timer)來驅(qū)動動畫會防止屏幕外動畫消耗不必要的資源甸怕,因為Flutter中屏幕刷新時會通知到綁定的SchedulerBinding,而Ticker是受SchedulerBinding驅(qū)動的腮恩,由于鎖屏后屏幕會停止刷新梢杭,所以Ticker就不會再觸發(fā)。
Curve
Animation對象在整個動畫執(zhí)行過程中輸出的值可以是線性的秸滴、曲線的武契、一個步進(jìn)函數(shù)或者任何其他曲線函數(shù)等等,這由Curve來決定荡含。CurvedAnimation也是Animation<double>類型咒唆。
final CurvedAnimation curve =
CurvedAnimation(parent: controller, curve: Curves.easeIn);
Tween
默認(rèn)情況下,AnimationController對象值的范圍是[0.0释液,1.0]全释。如果我們需要構(gòu)建UI的動畫值在不同的范圍或不同的數(shù)據(jù)類型,則可以使用Tween來添加映射以生成不同的范圍或數(shù)據(jù)類型的值误债。
// 要使用 Tween 對象浸船,需要調(diào)用其animate()方法
final Tween colorTween =
ColorTween(begin: Colors.transparent, end: Colors.black54);
animation = colorTween..animate(animation);
示例
class _AnimatedPageState extends State<AnimatedPage>
with SingleTickerProviderStateMixin {
late AnimationController controller; // 動畫控制器
late Animation<double> animation;
@override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(seconds: 2));
animation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
animation = Tween(begin: 0.0, end: 200.0).animate(animation)
..addListener(() {
setState(() {});
})
..addStatusListener((status) {
print("狀態(tài)值: ${status}");
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('動畫'),
),
body: Container(
padding: EdgeInsets.all(20),
child: ListView(
children: [
ElevatedButton(
onPressed: () {
controller.forward();
},
child: Text('開始動畫')),
Image.asset(
'assets/head.png',
width: animation.value,
height: animation.value,
),
],
),
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}