在動畫過程中,需要兩個部分:
- 第一個就是動畫控制類,它不需要知道在螢幕上顯示什麼。
- 第二個就是實際上的UI元素
1. 動畫控制類
動畫控制類有兩部分:
-
Animation
Animation
類物件是一個抽象類,支援泛型,我們常用的是Animation<double>
,當然這裡不僅僅是double,可以是任意一個物件。Animation物件本身和UI渲染沒有任何關係,而是用於生產動畫過程中的值,用這個值來控制動畫,也能獲取當前動畫的狀態(正在播放中,還是結束等),也能獲取動畫播放過程中的當前值。而且Animation
類有很多不同的子類,用於實現不同的動畫效果,我們下篇在將。 -
AnimationController
AnimationController
一方面是用來管理Animation
,比如動畫的開關,另一方面,AnimationController
和Animation
還有另外一種關係,就是AnimationController
是輸入,是X,它的範圍是從0.0到1.0的數字,經過Animation
裡定義的f(),對映到對應的Y值,
所以Animation
和AnimationController
是在動畫中比不可少的元素,而且這兩個一定是搭配使用的。看程式碼:
//定義的AnimationController,動畫時長2000ms
AnimationController controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
//Tween是Animation的子類,其實是定義了一種f(),Y的值是從50到200
//.animate(controller)是一定要呼叫的
Animation<double> animation = Tween(begin: 50.0, end: 200.0).animate(controller);
//動畫控制,開始播放
controller.forward();
複製程式碼
2 使用動畫
講完了動畫控制類,這個動畫控制類怎麼運用到UI元素上呢?
總共有三種方法:
- 原始方法
將Animation
的值直接用到widget的屬相上,用這種方法時,一定要記得Animation
裡要新增動畫的監聽addListener
方法,addListener
方法裡要呼叫setState(() {})
。
因為雖然Animation
的值變了,但如果不呼叫setState(() {})
的話,widget就不會重新繪製。
class AnimApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AnimAppState();
}
}
class _AnimAppState extends State<AnimApp> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
animation = Tween(begin: 50.0, end: 200.0).animate(controller)
..addListener(() {
setState(() {});
});
controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: animation.value,
height: animation.value,
decoration: BoxDecoration(color: Colors.redAccent),
),
));
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
複製程式碼
-
AnimatedWidget
AnimatedWidget
是Flutter將動畫封裝成了Widget,更方便使用,而且不需要呼叫setState()
,AnimatedWidget
也有很多子類,這次先不介紹,先看它的普通使用,AnimatedWidget
需要一個Animation
的引數:
class AnimApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AnimAppState();
}
}
class _AnimAppState extends State<AnimApp> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
animation = Tween(begin: 50.0, end: 200.0).animate(controller);
controller.repeat();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: AnimApp2(animation: animation,),
);
}
}
class AnimApp2 extends AnimatedWidget {
AnimApp2({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return Scaffold(
body: Center(
child: Container(
width: animation.value,
height: animation.value,
decoration: BoxDecoration(color: Colors.redAccent),
),
));
}
}
複製程式碼
-
AnimatedBuilder
前面的
AnimatedWidget
,我要是每實現一個動畫就單獨抽出來一個class檔案來寫Widget,太複雜了,所以有了AnimatedBuilder
,AnimatedBuilder
可以更方便的為Widget新增動畫,看程式碼:
class AnimApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AnimAppState();
}
}
class _AnimAppState extends State<AnimApp> with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
animation = Tween(begin: 50.0, end: 200.0).animate(controller);
controller.repeat();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Center(
child: Container(
width: animation.value,
height: animation.value,
decoration: BoxDecoration(color: Colors.redAccent),
),
);
},
),
);
}
}
複製程式碼