网络知识 娱乐 Flutter实战一个动画「教学」

Flutter实战一个动画「教学」

一、动画

原理

Flutter动画基于补间动画,在页面刷新时,会生成一个插值,随后根据插值不断更新RenderObject的状态,实现动画播放。

Flutter动画类型

  • 隐式动画:使用自带的Animation控件直接显示动画,无法对动画的状态做完整的监听和控制;
  • 显式动画:自定义Controller、Tween、Curve,可以完整监听和控制动画播放;

隐式动画

如AnimatedContainer、AnimateAlign:

nColor _color = Colors.blue;nn@overridenWidget build(BuildContext context) {n return AnimatedContainer(n duration: const Duration(seconds: 1),n width: 200,n height: 200,n color: _color,n );n}nnsetState(() {n _color = Colors.yellow;n});

隐式动画实际上是flutter对显式动画的一种封装,通常是ImplicitlyAnimatedWidget的子类,在内部还是利用显式动画的那一套工具实现的;

abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> n extends State<T> with SingleTickerProviderStateMixin<T> {nn /// The animation controller driving this widget's implicit animations.n @protectedn AnimationController get controller => _controller;n late final AnimationController _controller = AnimationController(n duration: widget.duration,n debugLabel: kDebugMode ? widget.toStringShort() : null,n vsync: this,n );nn /// The animation driving this widget's implicit animations.n Animation<double> get animation => _animation;n late Animation<double> _animation = _createCurve();nn ... n}

显式动画

  • 幕后主导:Ticker
  • 三大件:AnimationController、Tween、Curve
  • UI:AnimatedWidget(SlideTransition、ScaleTransition等)、AnimatedBuilder、自定义StatefulWidget

Ticker

帧定时器,负责驱动动画的运行,执行start方法后,每一帧渲染前(一个vsync信号)都会回调一次传入的函数。

Ticker ticker = Ticker((elapsed) => print('hello'));

直接操作Ticker比较复杂,因此创建动画控件时直接使用SingleTickerProviderStateMixin或TickerProviderStateMixin混入到动画控件的state中即可。

AnimationController

和它的名字一致,主要作用是控制和驱动动画的播放、停止等操作。

创建AnimationController时需要传入一个TickerProvider,在Ticker进行逐帧回调时,不断生成0.0 ~ 1.0之间的double值(插值)[相当于每帧刷新一次插值]。

AnimationController的播放控制能力也是通过修改当前插值实现的,如:

  • forward():实际上调用animateToInternal(upperBound),即将value增加到1.0;
  • reverse():实际上调用_animateToInternal(lowerBound),即将value减小到0.0;
  • repeat():反复将插值从最小值到最大值之间变换;
  • reset():将值调回到0.0。

Tween<T>

主要作用就是转换AnimationController的value。

原理上来说,利用AnimationController生成的value就可以实现动画效果了,但是AnimationController的值只能是double类型,如果动画需要实现控件的color、rect、alignment的变换,仅靠一个double值很难实现想要的效果。

给定一个begin和end值,Tween可以根据lerp方法,将AnimationController的value做一些转换。

Tween有很多子类,如ColorTween、RectTween、AlignmentTween等,基本上满足实现动画的需要。可以自定义数据类型的变换,需要重写lerp方法,实现自己的变换函数。

用法:Tween是Animatable的子类,通过animate方法可转为Animation类,绑定到AnimationController中。

Animation sizeAnimation = Tween<double>(n begin: 1.0,n end: 2.0,n).animate(_animController);nnAnimation colorAnimation = ColorTween(n begin: Colors.blue,n end: Colors.yellow,n).animate(_animController);

Curve

定义动画值的变化速度。AnimationController的value是线性速度生成的,Curve可以改变生成速度,达到加速、减速等动画效果。

系统内置了几十种不同Curve实例,这也是最常用的控制,也可以自己基于cubic(二阶贝塞尔)创建。

总结:

  • Ticker提供逐帧的回调;
  • AnimationController默认提供了从0.0到1.0的值变化,在Ticker回调时生成一个插值,用来进行动画控制;
  • 基于AnimationController的初始值,Tween对其进行转换,Curve对动画速度进行控制。
  • Tween的值包装进Animation对象中,提供了值和状态的监听,绑定到动画组件中实现动画效果。

动画Widget

通过AnimationController、Tween、Curve三大件,可以得到一个Animation对象;

以平移动画为例:

AnimationController _animController = AnimationController(n duration: Duration(second: 1);n vsync: this;n);nnAnimation sildeAnimation = Tween<Offset>(n begin: Offset.zero,n end: Offset(1, 1),n).animate(n CurvedAnimation(parent: _animController, curve: Curves.ease),n);

用法一:AnimatedWidget子类

SlideTransition(n position: sildeAnimation,n child: Container(n width: 200,n height: 200,n color: Colors.blue,n ),n);

用法二:AnimateBuilder

AnimatedBuilder(n animation: _animController,n builder: (context, child) {n return FractionalTranslation(n translation: sildeAnimation.value,n child: Container(n width: 200,n height: 200,n color: Colors.blue,n ),n );n },n);

用法三:StatefulWidget(需要手动setState())

_animController.addListener(() {n setState(() {n });n});nnFractionalTranslation(n translation: sildeAnimation.value,n child: Container(n width: 200,n height: 200,n color: Colors.blue,n ),n);

组合动画

1、顺序动画(同步)

TweenSequence按顺序执行每一个子Tween item,通过weigt参数控制每一个item的时间占比:

_offsetAnimation = TweenSequence([n TweenSequenceItem(n tween: Tween<Offset>(begin: Offset.zero, end: const Offset(1, 1)),n weight: 1,n ),n TweenSequenceItem(n tween: Tween<Offset>(begin: const Offset(1, 1), end: Offset.zero),n weight: 1,n ),n]).animate(_animController);

2、交织动画(异步)

通过Interval控制每个子动画的起始和终止的时间;

_offsetAnimation = Tween<Offset>(n begin: Offset.zero,n end: const Offset(2, 1),n).animate(n CurvedAnimation(n parent: _animController,n curve: const Interval(0.0, 1.0, curve: Curves.linear),n ),n);nn_colorAnimation = ColorTween(n begin: Colors.blue,n end: Colors.yellow,n).animate(n CurvedAnimation(n parent: _animController,n curve: const Interval(0.3, 0.7, curve: Curves.linear),n ),n);

二、游戏

以认单词游戏为例:动画数量多,控件数量多,状态管理复杂。

  1. 枚举动画的所有状态,分类整理成状态组,如人物动作、人物位置、地图位置等,每种状态组包含多个状态,例如人物动作中有站立、跑步、欢呼状态。
  2. 创建游戏剧本,将游戏中的每个状态按顺序保存,并使用链表结构将其串联;
  3. 创建GameController,持有游戏剧本,控制游戏剧本的走向; 将游戏的各个元素从Page中抽成子控件,如人物控件、地图控件、选择器控件等;
  4. 各个控件和GameController的通信通过Cubit实现;
  5. 控件中,通过BlocBuilder、BlocListener监听自己关心的状态,当监听游戏切换到自己的状态时,开始执行动画(或其他操作),结束后回调notifyStateFinish通知GameController,GameController会转到到下一个状态;
  6. GameController收到notifyStateFinish通知后,视为当前状态已经结束,按照游戏剧本切换到下一个状态,让Cubit去通知下一个组件,循环往复该过程;

认单词游戏属于线性游戏流程,也就是游戏的流程固定,不会受到用户的操作影响而改变游戏的走向。如果是多分支的游戏,可以将游戏剧本也做成链表结构,创建多个子剧本,GameController再根据游戏的具体走向,连接到下一个子剧本中,以实现切换分支的功能。 ———————————————— 版权声明:本文为CSDN博主「Wanderlust1」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_41007222/article/details/125944301

以上就是flutter的一个小动画的学习了,想成为flutter工程师还要学习许多;比喻:从最基础的语法dart、UI、线程、启动流程、framework框架、性能监控等等一系列的进阶学习。这里我推荐大家参考学习《flutter手册》里面是从最基础的语法dart开始教学。我建议新手或者想进阶flutter技术的,可以通过这个手册辅助自己进阶。

【私信:“手册”获取】Flutter手册

文末

Flutter是真正的跨平台不管是开发体验 、调试 、性能 (基本上比肩原生) 而我包含:IOS、安卓、Windows、Mac、Web,性能都是杠杠的,dart学习成本几乎很低,基本上一周就可以完全上手了,那些说学习成本很高的怕不是老了学不动了?在跨平台方面能做到这么好的目前也只有flutter了,有什么理由不用呢?