简介

一般指的是手机上60Hz频率的,那么如果需要动画流畅,则需要在1s中之内绘制60帧的组件,那么动画帧率超过1000ms/60=16 FPS,就基本能看了,超过 32 FPS就会感觉相对平滑,而超过 32 FPS,大多数人基本上就感受不到差别了。

AnimatedContainer

可以理解成带动画的组件,只需要设置其时间duration,那么其属性就会执行相应的动画。

  1. AnimatedContainer(
  2. duration: Duration(milliseconds: 300),
  3. width: 100,
  4. height: 32,
  5. child: Align(
  6. alignment: Alignment.center,
  7. child: ...,
  8. ),
  9. decoration: BoxDecoration(...)
  10. )

当然这是附在组件上的动画设置。

AnimatedSwitcher

不同控件的切换动画。

  1. AnimatedSwitcher(
  2. duration: Duration(seconds: 1),
  3. child: Text(
  4. 'Hello',
  5. key: UniqueKey(),
  6. ),
  7. );

如果是同一个控件,但是还是需要动画的话,就需要UniqueKey();如果需要在控件切换时,多个动画形式组合,则:

  1. AnimatedSwitcher(
  2. duration: Duration(seconds: 1),
  3. transitionBuilder: (child, animation) {
  4. return FadeTransition(
  5. opacity: animation,
  6. child: ScaleTransition(
  7. scale: animation,
  8. child: child,
  9. ),
  10. );
  11. },
  12. child: Text(
  13. 'Hello',
  14. key: UniqueKey(),
  15. ),
  16. );

transitionBuilder中动画组合嵌套即可。

Curve

动画的曲线运动方式类型。Flutter官方文档

TweenAnimationBuilder

创建一个补间动画的builder,需要返回一个Widget

  1. TweenAnimationBuilder(
  2. duration: Duration(seconds: 1),
  3. tween: Tween(begin: 0, end: 1),
  4. builder: (BuildContext context, dynamic value, Widget child) {
  5. return Opacity(
  6. opacity: value,
  7. child: Container(
  8. width: 300,
  9. height: 300,
  10. color: Colors.white,
  11. ),
  12. );
  13. },
  14. )

tween:指的是关键帧。
builder:指的是在关键帧中间创建补全帧。
value:指的是0-1之间的数值,就是tween参数中定义的。
注意:Tween(begin: 0, end: 1)中的数值是int``double都是可以的。
如果需要和不同的动画组合使用,应该怎么办呢?
例如添加一个平移动画:

  1. TweenAnimationBuilder(
  2. duration: Duration(seconds: 1),
  3. tween: Tween(begin: 100, end: 120),
  4. builder: (BuildContext context, dynamic value, Widget child) {
  5. return Container(
  6. width: 300,
  7. height: 300,
  8. color: Colors.blue,
  9. child: Center(
  10. child: Transform.translate(
  11. offset: Offset(10, -30),
  12. child: Text('Hello', style: TextStyle(fontSize: value),),
  13. ),
  14. ),
  15. );
  16. },
  17. )

计数器动画

  1. Center(
  2. child: Container(
  3. width: 300,
  4. height: 120,
  5. child: TweenAnimationBuilder(
  6. duration: Duration(seconds: 1),
  7. tween: Tween(begin: 0.0, end: 10.0),
  8. builder: (BuildContext context, dynamic value, Widget child) {
  9. final whole = value ~/ 1;
  10. final decimal = value - whole;
  11. return Stack(
  12. children: [
  13. Positioned(
  14. top: -100 * decimal,
  15. child: Opacity(
  16. opacity: 1.0 - decimal,
  17. child: Text(
  18. '$whole',
  19. style: TextStyle(fontSize: 100),
  20. ),
  21. ),
  22. ),
  23. Positioned(
  24. top: 100 - 100 * decimal,
  25. child: Opacity(
  26. opacity: decimal,
  27. child: Text(
  28. '${whole + 1}',
  29. style: TextStyle(fontSize: 100),
  30. ),
  31. ),
  32. ),
  33. ],
  34. );
  35. },
  36. ),
  37. ),
  38. )

主要是builder参数中返回方法,Positioned组件的位置动画+Opacity组件的透明度动画,这样就可以满足计数器增加或是减少时的对应的数字的运动改变动画。

封装成动画组件

  1. class AnimatedCounter extends StatelessWidget {
  2. final Duration duration;
  3. final int value;
  4. const AnimatedCounter({Key key, @required this.duration, @required this.value}) : super(key: key);
  5. @override
  6. Widget build(BuildContext context) {
  7. return TweenAnimationBuilder(
  8. duration: Duration(seconds: 1),
  9. tween: Tween(end: value),
  10. builder: (BuildContext context, dynamic value, Widget child) {
  11. final whole = value ~/ 1;
  12. final decimal = value - whole;
  13. return Stack(
  14. children: [
  15. Positioned(
  16. top: -100 * decimal,
  17. child: Opacity(
  18. opacity: 1.0 - decimal,
  19. child: Text(
  20. '$whole',
  21. style: TextStyle(fontSize: 100),
  22. ),
  23. ),
  24. ),
  25. Positioned(
  26. top: 100 - 100 * decimal,
  27. child: Opacity(
  28. opacity: decimal,
  29. child: Text(
  30. '${whole + 1}',
  31. style: TextStyle(fontSize: 100),
  32. ),
  33. ),
  34. ),
  35. ],
  36. );
  37. },
  38. );
  39. }
  40. }

AnimatedCounter对象中需要传入Duration和当前跳转的数字count

使用

  1. Center(
  2. child: Container(
  3. width: 300,
  4. height: 120,
  5. child: AnimatedCounter(duration: Duration(seconds: 1), value: count),
  6. ),
  7. )

当然这个方法中的count,是需要状态改变的,需要在setState方法中改变的。

特别说明

动画在程序启动的时候第一次才会用到begin的状态,之后的执行动画只会参考结束时的时候为参考位置,并到达新的end状态之间的动画。