Flutter嚐鮮2——動畫處理<基礎>
本例的程式碼參考 這裡 。
概述
動畫處理的基本原理是,對元件(widget)的某個或某組屬性設定一組連續變化的值,這些值在一定時間間隔內不斷被應用到該屬性上,使得元件的外觀看上去在進行平滑而連續的變動。
例如2秒內每隔0.1s將一個元件的x軸座標加1,那麼該元件看上去就是從左至右移動了2秒共20個單位。
處理組成部分
具體到Flutter,動畫處理主要分為三個部分:
- 動畫控制器(AnimationController),控制整個動畫執行,包括開始結束和動畫時長等。
- 動畫抽象(Animation),描述了動畫運動的速率,例如元件是加速還是勻速,或者其它變化。
- 變動範圍(Tween),定義了動畫元件屬性值的變化範圍,例如從座標(0, 0)移動到(20, 0)
處理流程
上述三大元件,控制了整個動畫的執行。用文字描述,其流程主要包括:
- 初始化動畫控制器,設定動畫的時長,初始值等(如上例:2秒時長)
- 初始化變動範圍(如上例:Offset從[0, 0]到[20, 0])
- 初始化動畫抽象,定義它的運動速率(如上例:勻速變動)
- 將動畫描述的值,賦值到動畫元件的對應屬性上
- 開始執行動畫(呼叫動畫控制器的開始方法)
- 動畫執行結束
AnimationController定義
AnimationController是一個特殊的Animation物件。建立一個AnimationController時,需要傳遞一個vsync引數。設定此引數的目的,是希望螢幕每一幀畫面變化時能夠被通知到。也就是說,螢幕重新整理的每一幀,AnimationController都會生成一個新的值(同樣也意味著,如果在螢幕外那麼就不被觸發)。這樣動畫元件就能夠完成一個連續平滑的動畫動作。
Tickers can be used by any object that wants to be notified whenever a frame triggers。
AnimationControler通常是在一個StatefulWidget中被宣告,並且附帶一個叫做SingleTickerProviderStateMixin的Mixin(原因就在上面說的,要設定vsync引數)。
class AnimationDemo extends StatefulWidget { AnimationDemoState createState() => AnimationDemoState(); } class AnimationDemoState extends State<AnimationDemo> with SingleTickerProviderStateMixin { AnimationController controller; @override void initState() { super.initState(); controller = AnimationController(duration: Duration(milliseconds: 2000), vsync: this); ... } @override void dispose() { controller.dispose();// 離開時需要銷燬controller super.dispose(); } ... }
當Animation和Tween的設定完成後,簡單呼叫controller.forward()即可開始動畫。
Tween定義
Tween就是要改變的屬性值的變動範圍。它可以是任意的屬性類如Offset或者Color,最常見的是double。
... AnimationController controller; Tween<double> slideTween = Tween(begin: 0.0, end: 20.0); ...
Animation定義
Animation物件本身可以看做是動畫中所有變化值的一個集合。它包含了變化區間內的所有可取值,並返回給動畫元件當前的變動值。
Animation在使用中要設定的,是他的變動速率,如Curves.linear(線性變化)。
... AnimationController controller; Tween<double> slideTween = Tween(begin: 0.0, end: 20.0); Animation<double> animation; @override void initState() { super.initState(); ... animation = slideTween.animate(CurvedAnimation(parent: controller, curve: Curves.linear)); } ...
動畫元件定義
為了說明簡單,在build方法中巢狀兩個Container元件,外部容器Container的paddingLeft跟隨動畫變動,達到移動內部Container的目的。
class AnimationDemoState extends State<AnimationDemo> with SingleTickerProviderStateMixin { ... @override Widget build(BuildContext context) { return Container( width: 200, alignment: Alignment.centerLeft, padding: EdgeInsets.only(left: animation.value), child: Container( color: Colors.blue, width: 80, height: 80, ), ); } }
啟動動畫
在啟動動畫之前有一個Flutter的基本概念要說明。做過React的同學很清楚,要想render方法重新執行,要麼props有更新要麼state有更新。在Flutter也同樣如此,build方法同樣依賴於state的更新才能重新執行。
在AnimationController的說明中,我們知道因為設定了vsync所以螢幕重新整理的每一幀都會更新它的值。所以可以在Controller上加上一個listener,每次有update都呼叫一下setState,以此達到重新渲染UI的目的。
... @override void initState() { ... animation.addListener(() => this.setState(() {})); controller.repeat(); // 動畫重複執行 }
呼叫controller.repeat()方法,動畫會被反覆執行。如果想只執行一次,那麼可以使用controller.forward();