【Flutter实战】动画核心(2/2)

时间:2022-07-24
本文章向大家介绍【Flutter实战】动画核心(2/2),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

老孟导读:这是Flutter动画系统核心的第二部分,接上一篇。这一篇主要讲解动画曲线、自定义动画曲线,以及AnimationController 、Tween 、Curve 三者之间的关系。

Curve

动画中还有一个重要的概念就是 Curve,即动画执行曲线。Curve 的作用和 Android 中的 Interpolator(差值器)是一样的,负责控制动画变化的速率,通俗地讲就是使动画的效果能够以匀速、加速、减速、抛物线等各种速率变化。

蓝色盒子大小 100 变大到 200,动画曲线设置为 bounceIn(弹簧效果)

class CurveDemo extends StatefulWidget {
  @override
  _CurveDemoState createState() => _CurveDemoState();
}

class _CurveDemoState extends State<CurveDemo>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation _animation;

  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1000))
          ..addListener(() {
            setState(() {});
          });

    _animation = Tween(begin: 100.0, end: 200.0)
        .chain(CurveTween(curve: Curves.bounceIn))
        .animate(_controller);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          _controller.forward();
        },
        child: Container(
          height: _animation.value,
          width: _animation.value,
          color: Colors.blue,
          alignment: Alignment.center,
          child: Text(
            '点我变大',
            style: TextStyle(color: Colors.white, fontSize: 18),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }
}

动画加上Curve 后,AnimationController 的最小/大值必须是 [0,1]之间,例如下面的写法就是错误的:

_controller =
    AnimationController(vsync: this, duration: Duration(milliseconds: 1000),lowerBound: 100.0,upperBound: 200.0)
      ..addListener(() {
        setState(() {});
      });
_animation = CurveTween(curve: Curves.bounceIn).animate(_controller);

抛出如下异常:

正确写法:

_controller =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1000))
          ..addListener(() {
            setState(() {});
          });

    _animation = Tween(begin: 100.0, end: 200.0)
        .chain(CurveTween(curve: Curves.bounceIn))
        .animate(_controller);

系统已经提供了38种常用到动画曲线:

linear

decelerate

bounceIn**

其余动画效果可到 Curve 源代码中查看。

通常情况下,这些曲线能够满足 99.99% 的需求,很多时候设计也就是告诉你动画 先快后慢 或者 先慢后快,所以选个类似的就可以了,但有一些 特别 的设计非要一个系统没有的动画曲线,要怎么办?

那就自定义一个动画曲线

其实自定义一个动画曲线难点在数学上,怎么把数学公式用代码实现才是难点。

下面是一个 楼梯效果 的动画曲线:

自定义动画曲线需要继承 Curve 重写 transformInternal 方法即可:

class _StairsCurve extends Curve {

  @override
  double transformInternal(double t) {
    return t;
  }
}

直接返回 t 其实就是线性动画,即 Curves.linear,实现楼梯效果动画代码如下:

class _StairsCurve extends Curve {
  //阶梯的数量
  final int num;
  double _perStairY;
  double _perStairX;

  _StairsCurve(this.num) {
    _perStairY = 1.0 / (num - 1);
    _perStairX = 1.0 / num;
  }

  @override
  double transformInternal(double t) {
    return _perStairY * (t / _perStairX).floor();
  }
}

修改开始处的案例,使用此曲线:

_animation = Tween(begin: 100.0, end: 200.0)
    .chain(CurveTween(curve: _StairsCurve(5)))
    .animate(_controller);

总结

动画系统的核心是 AnimationController,而且是不可或缺的,动画中必须有 AnimationController,而 Tween 和 Curve 则是对 AnimationController 的补充, Tween 实现了将 AnimationController [0,1]的值映射为其他类型的值,比如颜色、样式等,Curve 是 AnimationController 动画执行曲线,默认是线性运行。

将 AnimationController 、 Tween 、Curve 进行关联的方式:

AnimationController _controller;
Animation _animation;

@override
void initState() {
  super.initState();
  _controller =
      AnimationController(vsync: this, duration: Duration(milliseconds: 1000))
        ..addListener(() {
          setState(() {});
        });

  _animation = Tween(begin: 100.0, end: 200.0)
      .animate(_controller);
}

或者:

_animation = _controller.drive(Tween(begin: 100.0, end: 200.0));

加入 Curve :

_animation = Tween(begin: 100.0, end: 200.0)
    .chain(CurveTween(curve: Curves.linear))
    .animate(_controller);

或者:

_animation = _controller
    .drive(CurveTween(curve: Curves.linear))
    .drive(Tween(begin: 100.0, end: 200.0));

只需要 Curve :

_animation = CurveTween(curve: Curves.linear)
    .animate(_controller);

或者

_animation = _controller.drive(CurveTween(curve: Curves.linear));

一个 AnimationController 可以对应多个 Animation(Tween 或者 Curve),StatefulWidget 组件可以包含多个 AnimationController ,但 SingleTickerProviderStateMixin 需要修改为 TickerProviderStateMixin,改变颜色和大小,由两个 AnimationController 控制:

class MultiControllerDemo extends StatefulWidget {
  @override
  _MultiControllerDemoState createState() => _MultiControllerDemoState();
}

class _MultiControllerDemoState extends State<MultiControllerDemo>
    with TickerProviderStateMixin {
  AnimationController _sizeController;
  AnimationController _colorController;
  Animation<double> _sizeAnimation;
  Animation<Color> _colorAnimation;

  @override
  void initState() {
    super.initState();
    _sizeController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 2000))
          ..addListener(() {
            setState(() {});
          });

    _sizeAnimation = _sizeController
        .drive(CurveTween(curve: Curves.linear))
        .drive(Tween(begin: 100.0, end: 200.0));

    _colorController =
        AnimationController(vsync: this, duration: Duration(milliseconds: 1000))
          ..addListener(() {
            setState(() {});
          });

    _colorAnimation = _colorController
        .drive(CurveTween(curve: Curves.bounceIn))
        .drive(ColorTween(begin: Colors.blue, end: Colors.red));
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: GestureDetector(
        onTap: () {
          _sizeController.forward();
          _colorController.forward();
        },
        child: Container(
          height: _sizeAnimation.value,
          width: _sizeAnimation.value,
          color: _colorAnimation.value,
          alignment: Alignment.center,
          child: Text(
            '点我变化',
            style: TextStyle(color: Colors.white, fontSize: 18),
          ),
        ),
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _sizeController.dispose();
    _colorController.dispose();
  }
}

AnimationController 、Tween 、Curve 是整个动画的基础,Flutter 系统封装了大量了动画组件,但这些组件也是基于此封装的,因此深入了解这三部分比学习使用动画组件更重要,再次对这3个进行总结:

  • AnimationController:动画控制器,控制动画的播放、停止等。继承自Animation< double >,是一个特殊的Animation对象,默认情况下它会线性的生成一个0.0到1.0的值,类型只能是 double 类型,不设置动画曲线的情况下,可以设置输出的最小值和最大值。
  • Curve:动画曲线,作用和Android中的Interpolator(差值器)类似,负责控制动画变化的速率,通俗地讲就是使动画的效果能够以匀速、加速、减速、抛物线等各种速率变化。
  • Tween:将 AnimationController 生成的 [0,1]值映射成其他属性的值,比如颜色、样式等。

完成一个动画效果的过程如下:

  1. 创建 AnimationController
  2. 如果需要 Tween 或者 Curve,将 AnimationController 与其关联,Tween 和 Curve 并不是必须的,当然大部分情况都需要。
  3. 将动画值作用于组件,当没有Tween 和 Curve 时,动画值来源于AnimationController,如果有 Tween 和 Curve,动画值来源于 Tween 或者Curve 的 Animation。

如果你发现阅读完此篇文章还是感觉不会写动画,不要灰心,这是正常的,第一次想了解这些抽象的概念还是比较困难的,如果你有其他平台的相关经验,那会好很多,对于动画,想要掌握个人认为只有一个方法就是 多写

后面会介绍动画组件基础使用、实现原理、高级动画以及自定义动画,把每一个动画组件的用法都亲自手写一遍(而不是复制黏贴),回过头来在看这篇文章,会有不一样的感觉。