• 隐式动画解读


    Implicitly: 隐式动画, 根据文件里的描述它主要是用来修改widget在某个是个的属性, 通俗一点讲也能算作是属性动画
    就是在动画事件内, 根据定义的时间曲线获取对应的补间值, 然后不断的更新widget的配置信息(如果有变动), 在vsync扫描时就会不断的更新每一帧的界面
    连在一起看就成了动画效果, 实现它需要有时间函数曲线. 补间值, vsync

    1. Vsync

    • Vsync信息由flutter engine提供, 当前的widget只需要with一个 TickerProviderStateMixin 类, 就能监听到系统的vsync信号, 以下面的 SingleTickerProvider 为栗子, 它直接控制和监听 SchedulerBinding.instance 来实现vsync的回调事件转发给AnimationController
    mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
     
      //创建ticker,这里创建了一个tiker,而 `TickerProvider` 是个抽象类,所以vsync肯定和它有关
      @override
      Ticker createTicker(TickerCallback onTick) { ...
        _ticker = Ticker(onTick, debugLabel: kDebugMode ? 'created by $this' : null);
    
      @override
      void didChangeDependencies() { ...
          _ticker.muted = !TickerMode.of(context);
    }
    
    class Ticker {
      //Ticker的初始化只有一个参数,没有继承其他的内
      Ticker(this._onTick, { this.debugLabel }) {
    
      //控制ticker的事件队列,确保次序正确
      TickerFuture _future;
    
      //决定当前ticker是否只是监听vsync不执行callback,避免视图不现实的内存开销
      set muted(bool value) {
          isMuted ...
          unscheduleTick();
          shouldScheduleTick ...
          scheduleTick();
    
      //开启一个定时刷新,如果当前能执行动画(!isMetued)则执行它的callback事件
      TickerFuture start() {
        ...
        if (shouldScheduleTick) {
          scheduleTick();
        ...
        return _future;
      }
    
      void stop({ bool canceled = false }) { ...
        unscheduleTick();
        if (canceled) {
          localFuture._cancel(this);
        } else {
          localFuture._complete();
        }
      }
    
      //记录animationId,避免重复schedule
      @protected
      bool get scheduled => _animationId != null;
      
      //执行tick的三要素
      @protected
      bool get shouldScheduleTick => !muted && isActive && !scheduled;
      
      //
      void _tick(Duration timeStamp) {..
        _onTick(timeStamp - _startTime);
        if (shouldScheduleTick)
          scheduleTick(rescheduling: true);
      }
    
      //最终将tick事件发送给 `SchedulerBinding.instance` ,持续定于window的每一帧
      @protected
      void scheduleTick({ bool rescheduling = false }) { ...
        _animationId = SchedulerBinding.instance.scheduleFrameCallback(_tick, rescheduling: rescheduling);
      }
      @protected
      void unscheduleTick() { ...
          SchedulerBinding.instance.cancelFrameCallbackWithId(_animationId);
      }
      //用于接管另外一个ticker,释放原来的ticker,使用自己的ticker事件
      void absorbTicker(Ticker originalTicker) {...
          if (shouldScheduleTick)
            scheduleTick();
          originalTicker._future = null; // so that it doesn't get disposed when we dispose of originalTicker
          originalTicker.unscheduleTick();
        }
        originalTicker.dispose();
      }
    

    2. 动画的时间曲线

    • 通过Widget初始化时构造一个AnimationController, 它接收一个总的时常, 同是提供了ticker构造工厂函数, 而ticker可以监听vsync信号,
    • 通过监听vsync事件和frameCallback的duration, 再结合动画的时间区间, 计算出每一帧绘制时的时间, 这样就生成一个动画的时间曲线
    • 除了生成时间曲线以外, 它还提供了动画控制的能力, 比如重复开始, 反转, 每执行一个操作它都会去从新生成一个当前的时间值, 确保动画的进度按照代码设定的逻辑执行
    class _AnimatedWidgetDemoState extends State<AnimatedWidgetDemo>
        with SingleTickerProviderStateMixin {
      AnimationController _animationController;
      @override
      void initState() {
        super.initState();
        _animationController = AnimationController(
          vsync: this,
          duration: Duration(seconds: 2),
        )..repeat();
      }
    //在AnimationController创建的时候,会创建ticker
    class AnimationController ...
    AnimationController({ ...
        _ticker = vsync.createTicker(_tick);
        ...
      }
      void _tick(Duration elapsed) { ...
        //事件比例计算,也就是当前的动画进度
        _value = _simulation.x(elapsedInSeconds).clamp(lowerBound, upperBound) as double;
        notifyListeners();
        _checkStatusChanged();
      }
    //重复执行
    TickerFuture repeat({ double min, double max, bool reverse = false, Duration period }) {
        ...
        stop();  
        return _startSimulation(_RepeatingSimulation(_value, min, max, reverse, period, _directionSetter));
      }
      //开始执行动画
    TickerFuture forward({ double from }) { ...  
        _direction = _AnimationDirection.forward;
        return _animateToInternal(upperBound);
      }
      TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear }) {...
        stop(); ...
        return _startSimulation(_InterpolationSimulation(_value, target, simulationDuration, curve, scale));
      }
     TickerFuture _startSimulation(Simulation simulation) { ...
        _value = simulation.x(0.0).clamp(lowerBound, upperBound) as double;
        final TickerFuture result = _ticker.start();
        _checkStatusChanged();
        return result;
      }
      //可以看到AnimationController对动画的控制最终都会传递给 `Ticker` ,它主要负责时间进度的计算,计算完毕后根据代码设定的逻辑通知ticker是否需要监听下一帧的回调事件。
    

    3. 补间值

    它代表的是动画在每一帧渲染时, Widget(RenderObject的最终提交渲染信息)的属性的值, 简单的动画一般只有开始和结束2个值
    动画过程也是线性的, 随着时间的推移, 按线性规律逐步完成的, 为了动画过渡的更加完美, 需要额外对补间值进行加工, 在保证时间因子是线性移动的前提下, 通过引入Curve函数, 每个时间节点的 属性值按照重新计算, 这样我们的补间值就变得丰富多样。
    按照这个规律我们可以把线性的补间值变成各种变化规律的补间值
    在flutter仓库的源码中定义了很多的动画

    • 补间值转换, 在flutter的动画设计中可以把它看作是一个以时间为 X 轴, 动画进度为 Y 的函数, 函数的具体实现都封装在 transform方法中

    基于此规律我们可以定义自己Curve, 生成自己的曲线函数, 这样就能实时的调整动画的补间值, 作出各种复杂的动画

    ParametricCurve (curves.dart)
        _BottomSheetSuspendedCurve (scaffold.dart)
        _BottomSheetSuspendedCurve (bottom_sheet.dart)
        Curve2D (curves.dart)
            CatmullRomSpline (curves.dart)
        Curve (curves.dart)
            FlippedCurve (curves.dart)
            _Linear (curves.dart)
            ElasticInOutCurve (curves.dart)
            SawTooth (curves.dart)
            Cubic (curves.dart)
            ElasticOutCurve (curves.dart)
            Interval (curves.dart)
            ElasticInCurve (curves.dart)
            _BounceInOutCurve (curves.dart)
            CatmullRomCurve (curves.dart)
            _BounceOutCurve (curves.dart)
            Threshold (curves.dart)
            _DecelerateCurve (curves.dart)
            _BounceInCurve (curves.dart)
    
    • 补间值的具体类, 基本上满足了绝大部分的场景, 直接就能使用
    Animatable (tween.dart)
        Tween (tween.dart)
            AlignmentTween (tweens.dart)
            FractionalOffsetTween (tweens.dart)
            AlignmentGeometryTween (tweens.dart)
            RelativeRectTween (transitions.dart)
            Matrix4Tween (implicit_animations.dart)
            BoxConstraintsTween (implicit_animations.dart)
            EdgeInsetsTween (implicit_animations.dart)
            DecorationTween (implicit_animations.dart)
            BorderTween (implicit_animations.dart)
            BorderRadiusTween (implicit_animations.dart)
            TextStyleTween (implicit_animations.dart)
            EdgeInsetsGeometryTween (implicit_animations.dart)
            ThemeDataTween (theme.dart)
            _FontWeightTween (sliding_segmented_control.dart)
            ShapeBorderTween (material.dart)
            _InputBorderTween (input_decorator.dart)
            MaterialPointArcTween (arc.dart)
            ReverseTween (tween.dart)
            StepTween (tween.dart)
            SizeTween (tween.dart)
            ColorTween (tween.dart)
            ConstantTween (tween.dart)
            IntTween (tween.dart)
            RectTween (tween.dart)
                MaterialRectCenterArcTween (arc.dart)
                MaterialRectArcTween (arc.dart)
    

    了解了Flutter动画的三要素之后, 来看看它的具体应用

    下面以最常用的 AnimatedContainer 为栗子, 通过它来构造一个动画, 下面是我写的一个简易的Demo, 复制粘贴就能运行起来
    通过点击手势来触发ValueListenableBuilder重新构建, 切换动画的的初始值

    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    
    class AnimatedContainerDemo extends StatelessWidget {
      final CheckStatusListener statusListener = CheckStatusListener(true);
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTap: () {
            statusListener.value = !statusListener.value;
          },
          child: ValueListenableBuilder<bool>(
              valueListenable: statusListener,
              builder: (BuildContext context, bool selected, Widget child) {
                return Center(
                  child: AnimatedContainer(
                     selected ? 200.0 : 100.0,
                    height: selected ? 100.0 : 200.0,
                    color: selected ? Colors.red : Colors.blue,
                    alignment: selected
                        ? Alignment.center
                        : AlignmentDirectional.topCenter,
                    duration: Duration(seconds: 2),
                    curve: Curves.fastOutSlowIn,
                    child: FlutterLogo(size: 75),
                  ),
                );
              }),
        );
      }
    }
    
    class CheckStatusListener extends ValueNotifier<bool>
        implements ValueListenable<bool> {
      CheckStatusListener(bool isSelected) : super(isSelected);
    }
    
    • 它的动画是否系统提供的AnimatedContainer实现,从构造方法就可以看出它支持设置多种不同的补间值
    class AnimatedContainer extends ImplicitlyAnimatedWidget {
    AnimatedContainer({
        Key key,
        this.alignment,
        this.padding,
        Color color,
        Decoration decoration,
        this.foregroundDecoration,
        double width,
        double height,
        BoxConstraints constraints,
        this.margin,
        this.transform,
        this.child,
        Curve curve = Curves.linear,
        @required Duration duration,
        VoidCallback onEnd,
      }) 
      ...
    class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {
      AlignmentGeometryTween _alignment;
      EdgeInsetsGeometryTween _padding;
      DecorationTween _decoration;
      DecorationTween _foregroundDecoration;
      BoxConstraintsTween _constraints;
      EdgeInsetsGeometryTween _margin;
      Matrix4Tween _transform;
    
    //它的父类也是一个空壳,只是包装了setState方法,这个方法会触发这个widget及children重建,慎用,不要将复杂过多的逻辑用这种方案时间,一般使用与小部件的widget,继续向上查找它的Ancestor
    abstract class AnimatedWidgetBaseState<T extends ImplicitlyAnimatedWidget> extends ImplicitlyAnimatedWidgetState<T> {
      @override
      void initState() {
        super.initState();
        controller.addListener(_handleAnimationChanged);
      }
    
      void _handleAnimationChanged() {
        setState(() { /* The animation ticked. Rebuild with new animation value */ });
      }
    }
    
    //父类实现了tickerProvider协议,可以通过Tiker发送监听屏幕刷新事件,同时也持有了`AnimationController`用于控制基本的动画行为
    abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
    
      @protected
      AnimationController get controller => _controller;
      Animation<double> _animation;
    
      @override
      void initState() {
        super.initState();
        _controller = AnimationController( ...
        _controller.addStatusListener((AnimationStatus status) { ...
        _updateCurve(); //这里将 Curve函数和时间关联在一起,它和Curve将Tween关联在一起是一样的,因为最终都是通过3个数的乘积的到最终补间值
        _constructTweens(); //初始化设置子类的Tween
        didUpdateTweens(); //供子类在视图更新时刷新Tween
      }
    
      @override
      void didUpdateWidget(T oldWidget) {
        super.didUpdateWidget(oldWidget);
          ...
          _updateCurve();
        _controller.duration = widget.duration;
        if (_constructTweens()) {
          forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
            _updateTween(tween, targetValue);
            return tween;
          });
          _controller
            ..value = 0.0
            ..forward();
          didUpdateTweens();
        }
      }
      
      //更新widget自带的curve函数,更新动画的时间进度,间接修改补间值
      void _updateCurve() { ...
          _animation = CurvedAnimation(parent: _controller, curve: widget.curve); 
      
      //自动更新补间动画值
      void _updateTween(Tween<dynamic> tween, dynamic targetValue) {
          ..begin = tween.evaluate(_animation)
          ..end = targetValue;
      }
      
      //充分利多态的特性,实现子类Tween的构造,并执行
      bool _constructTweens() {
        bool shouldStartAnimation = false;
        forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
          if (targetValue != null) {
            tween ??= constructor(targetValue);
            if (_shouldAnimateTween(tween, targetValue))
              shouldStartAnimation = true;
          } else {
            tween = null;
          }
          return tween;
        });
        return shouldStartAnimation;
      }
    
      //子类通过重写这个方法
      //1. 子类传递 `Tween<T> tween, T targetValue,`给父类,举个栗子,Tween<T> tween就是子类的 AlginmentTween,targetValue就是Aliginment.topLeft
      //2. 父类调用`constructor`方法,子类生成对应的AlginmentTween,这样子类就持有了AlginmentTween,在子类的build方法中获取父类的`animation`,就能实时的改变Contianer相关的属性了(此处的tween回传给父类主要是为了检测动画进度)
      //typedef TweenVisitor<T> = Tween<T> Function(Tween<T> tween, T targetValue, TweenConstructor<T> constructor);
      @protected
      void forEachTween(TweenVisitor<dynamic> visitor);
    
      //用于hook子视图在widgetupdate时更新补间值
      @protected
      void didUpdateTweens() { }
    }
    
    • 子类_AnimatedContainerState在每次构建中更新当前的补间值
    class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> {...
      @override
      Widget build(BuildContext context) {
        return Container(
          child: widget.child,
          alignment: _alignment?.evaluate(animation),
          padding: _padding?.evaluate(animation),
          decoration: _decoration?.evaluate(animation),
          foregroundDecoration: _foregroundDecoration?.evaluate(animation),
          constraints: _constraints?.evaluate(animation),
          margin: _margin?.evaluate(animation),
          transform: _transform?.evaluate(animation),
        );
      }
    
    • 创建Tween的具体实现

    在父类初始化是触发forEachTween来创建子类的Tween

    abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> { ...
    
    void initState() { ...
         _constructTweens(); -> forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
    
    void didUpdateWidget(T oldWidget) {...
      forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
    
    子类重写`forEachTween`,传入当前的`_alignmentTween`(父类动画进度检查用)和`widget.alignmentValue`
    class _AnimatedContainerState extends AnimatedWidgetBaseState<AnimatedContainer> { ...
    @override
      void forEachTween(TweenVisitor<dynamic> visitor) {
        _alignment = visitor(_alignment, widget.alignment, (dynamic value) => 
        AlignmentGeometryTween(begin: value as AlignmentGeometry)) as AlignmentGeometryTween;//用于父类构造通过Tween开启动画用
        ...
      }
    

    小结论

    理解系统隐私动画的封装主要是要了解forEachTween的执行过程,它是一个嵌套函数,同时实现了父子协同工作的方式。

    • 子类实现具体Tween的创建,并提交给父类,由父类来完成基本的动画操作

    • 子类在通过animation获取到当前的动画进度,结合对应的Tween生成新的补间值

    • AnimationController通过持有Ticker,可以很方便的注册和取消系统下一帧的回调,实时的计算当前的动画进度

  • 相关阅读:
    Hadoop-HA配置详细步骤
    Oracle 事务
    2 Oracle用户和表空间
    oracle 客户端监听文件配置
    Django 关联管理器
    第2讲 | 网络分层的真实含义是什么?
    hihoCoder #1151 : 骨牌覆盖问题·二 (矩阵快速幂,DP)
    poj 2411 Mondriaan's Dream (轮廓线DP)
    SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)
    HYSBZ 1086 王室联邦 (树的分块)
  • 原文地址:https://www.cnblogs.com/wwoo/p/yin-shi-dong-hua-jie-du.html
Copyright © 2020-2023  润新知