写点什么

Flutter 浅尝 Flare / Lottie / SVGA 多种动画模式

作者:阿策小和尚
  • 2021 年 12 月 05 日
  • 本文字数:7599 字

    阅读完需:约 25 分钟

Flutter 浅尝 Flare / Lottie / SVGA 多种动画模式

    动画在应用中是非常常见的,Flutter 不仅提供了多种 Animated 相关的动画模式,还支持多种三方的动画模式,和尚今天尝试一下 Flare / Lottie / SVGA 三种动画模式;

Flare

    FlareFlutter 的动画插件名称,全名是 flare_flutterFlare 可以为 App/游戏/网页等制作酷炫的矢量动画模型;Rive 是制作 Flare 动画的网站,在这里不仅可以自由定制动画,还可以免费下载大量开源的优秀动画;Flare 动画的优势是有效减少文件体积且获取极好的动画效果,适用于与场景交互不大的场景;

1. 初识 Flare

    和尚刚开始学习 Flare,官网注册账号成功后,便可以访问 NimaFlare 文件,Flare 动画包括两种;官网对于不同类型的资源有不同图标区分;


  1. Nima 为较旧格式,仅支持光栅图;主要是为游戏引擎和应用构建 2D 动画;

  2. Flare 为较新格式,支持矢量图与光栅图;主要为 AppWeb 构建高效动画,也可用于游戏设计;



    对于动画的制作过程,和尚暂不介绍,一是每个人的使用不同,二是和尚也在摸索过程,设计一个满意的动画需要精心设计与调整;资源区分公开和私有版,可根据右下角是否有 follow 箭头区分,和尚仅尝试一下开源的动画;



    和尚选择一个开源的动画进入详情页,可以在 open in flutter 中进行自定义调整;可以添加或处理资源样式动画的贝塞尔曲线等,同时根据需求处理是否循环播放,可减少代码中处理;



2. 集成方式

    和尚尝试的是 Flare 格式的动画,将 .flr 动画资源添加到本地资源库 images 中;若使用的是 Nima 格式的动画资源,可以尝试 nima 插件;

1. pubspec.yaml 中添加依赖库

dependencies:    flare_flutter: ^1.5.2
复制代码

2. 在文件中添加引用库

import "package:flare_flutter/flare_actor.dart";
复制代码

3. API 调用实现

    插件中提供了方便的 Widget 可以方便调用;


FlareActor('images/flare_boll.flr', animation: 'Bounce')
复制代码


分析源码:


const FlareActor(this.filename,    {this.boundsNode,      this.animation,      this.fit = BoxFit.contain,      this.alignment = Alignment.center,      this.isPaused = false,      this.snapToEnd = false,      this.controller,      this.callback,      this.color,      this.shouldClip = true});
复制代码


    filename 用来加载本地动画资源;


    animation 为制作动画过程中动画名称,且区分大小写,所以建议在编辑动画时动画名称更明确,若没有 animation 参数或内容有误,最终展示的时动画的第一帧;


  • fit 动画填充样式;

  • alignment 动画对齐方式;

  • isPause true 为暂停,false 为继续;

  • snapToEnd true 为直接跳到动画最后一帧,false 为正常播放;

  • color 动画颜色,若整体颜色为纯色可尝试使用,否则会覆盖其他设计颜色;

  • controller 控制器,可以通过 controller 控制动画的播放暂停或到具体的动画结点等,灵活方便;

  • callback 动画播放完成的回调;当动画设置的是循环播放则无法监听;当动画为非循环模式时,播放完成第一遍后可监听结果;和尚测试若再次 play('animation') 时动画会重新播放一次,不会一直循环重复,该监听方法只有一次;


class _FlareStatePage extends State<FlareStatePage> {  FlareControls _controls = FlareControls();  bool isPause = false, snapToEnd = false;
@override Widget build(BuildContext context) { return Scaffold( body: Column(children: <Widget>[ Expanded( child: FlareActor('images/flare_question.flr', animation: 'default', alignment: Alignment.center, fit: BoxFit.contain, controller: _controls, isPaused: isPause, snapToEnd: snapToEnd, callback: (name) { if (name == 'default') { _controls.play('default'); } }), flex: 1), Row(children: <Widget>[ _itemBtn('start'), _itemBtn('pause'), _itemBtn('resume'), _itemBtn('stop') ]), Expanded( child: Column(children: <Widget>[ Expanded( child: FlareActor('images/flare_boll.flr', animation: 'Bounce')), Expanded( child: FlareActor('images/flare_boll.flr', animation: 'Bounce', color: Colors.orange)) ]), flex: 1) ])); }
Widget _itemBtn(str) { return Expanded( child: Container( margin: EdgeInsets.all(1.0), child: FlatButton( color: Colors.lightBlueAccent, child: Text(str), onPressed: () { if (str == 'start') { isPause = false; snapToEnd = false; _controls.play('default'); } else if (str == 'pause') { isPause = true; } else if (str == 'resume') { isPause = false; } else if (str == 'stop') { snapToEnd = true; } setState(() {}); }))); }}
复制代码


Lottie

    LottieAndroidiOS 中的应用非常广泛,同时也支持 Flutter 等平台,和尚在官网查询之后发现官网推荐了两个开源的 Lottie 插件,和尚对其中的 https://github.com/simolus3/fluttie 进行学习尝试;


    和尚首先在 lottiefiles 中下载了两个酷炫的动画 json,我们也可以选择合适的动画进行编辑调整;跟原生一样,可以随心设计,当然这项重任还是要交给视觉设计的小姐姐比较好;



1. 集成方式

    集成方式都是统一的三大步骤;再此之前可以将下载的 json 文件添加到本地 images 资源文件夹中;

1. 在 pubspec.yaml 中添加依赖库

dependencies:    fluttie: ^0.3.2
复制代码

2. 在文件中添加引用库

import 'package:fluttie/fluttie.dart';
复制代码

3. 根据 API 调用

  1. 通过 loadAnimationFromAsset 异步加载本地 json 资源,或通过 loadAnimationFromJson 直接加载 json 内容;


void prepareLottie() async {  var instance = Fluttie();  var whaleLottie = await instance.loadAnimationFromAsset('images/animation_demo01.json');}
复制代码


  1. 设置 FluttieAnimationController 控制器,绑定动画资源,并设置动画的基本属性;


  • prepareAnimation 的固定参数是动画资源,不可缺少;

  • repeatCount 可设置动画重复的频率;RepeatCount.nTimes(n) 重复 n+1 次;RepeatCount.infinite() 无限循环播放;RepeatCount.dontRepeat() 仅一次,播放完停止;

  • repeatMode 可设置动画播放模式,START_OVER 播放完从头再次播放,REVERSE 从无到有从有到无;

  • duration 可设置动画播放时长;当设置无限重复时不生效;其余根据重复频率使单次动画时长均分;

  • preferredSize 可设置动画预加载大小,并不直接控制 Widget 大小;


whaleController = await instance.prepareAnimation(    whaleLottie,    repeatCount: const RepeatCount.infinite());
复制代码


  1. 开启动画即可准备好动画的基本要素;


setState(() { whaleController.start(); });
复制代码


  1. 将动画绘制在 Widget 即可初步成功;


@overrideWidget build(BuildContext context) {  return Scaffold(      body: Center(          child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[    Container( width: 280.0, height: 200.0,        child: FluttieAnimation(whaleController))    )  ])));}
复制代码


  1. 我们也可以动态监听动画状态并进行处理;


  • start() 从头开启动画;

  • pause() 暂停动画;

  • unpause() 从暂停处继续播放动画;

  • stopAndReset() 停止动画,rewindtrue 时结束动画并到动画开始时第一帧;false 为技术动画并到动画最后一帧;


Row(children: <Widget>[  Expanded( flex: 1,      child: FlatButton(          onPressed: () { starController.start(); },          child: Text('start'))),  Expanded( flex: 1,      child: FlatButton(          onPressed: () { starController.pause(); },          child: Text('pause'))),  Expanded( flex: 1,      child: FlatButton(          onPressed: () { starController.unpause(); },          child: Text('resume'))),  Expanded( flex: 1,      child: FlatButton(          onPressed: () { starController.stopAndReset(rewind: true); },          child: Text('stop')))])
复制代码



2. 注意事项

1. dispose() 动画

    动画对应用内存占用较大,建议在页面销毁或关闭时将动画销毁;


@overridevoid dispose() {  super.dispose();  whaleController?.dispose();  starController?.dispose();}
复制代码

2. dispose() 与 stopAndReset() 区别

    stopAndReset() 方法用来控制动画的停止状态,资源依然存在内存中,之后可继续操作动画的状态;


    dispose() 方法用来停止动画并释放资源,之后不能再操作动画状态;


class _LottieStatePage extends State<LottieStatePage> {  FluttieAnimationController whaleController, starController;
@override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[ Container( width: 280.0, height: 200.0, child: FluttieAnimation(whaleController)), Container(child: FluttieAnimation(starController)), Row(children: <Widget>[ Expanded( flex: 1, child: FlatButton( onPressed: () { starController.start(); }, child: Text('start'))), Expanded( flex: 1, child: FlatButton( onPressed: () { starController.pause(); }, child: Text('pause'))), Expanded( flex: 1, child: FlatButton( onPressed: () { starController.unpause(); }, child: Text('resume'))), Expanded( flex: 1, child: FlatButton( onPressed: () { starController.stopAndReset(rewind: false); }, child: Text('stop'))) ]) ]))); }
@override void dispose() { super.dispose(); whaleController?.dispose(); starController?.dispose(); }
@override void initState() { super.initState(); prepareLottie(); }
void prepareLottie() async { var instance = Fluttie(); var whaleLottie = await instance.loadAnimationFromAsset('images/animation_demo01.json'); whaleController = await instance.prepareAnimation( whaleLottie, repeatCount: const RepeatCount.nTimes(2));
var starLottie = await instance.loadAnimationFromAsset('images/star.json'); starController = await instance.prepareAnimation(starLottie, repeatCount: const RepeatCount.infinite(), repeatMode: RepeatMode.START_OVER);
setState(() { whaleController.start(); starController.start(); }); }}
复制代码



SVGA

    和尚之前尝试了 FlareLottie 动画,实现效果都很高效;今天和尚尝试另一种思路 SVGA 动画;SVGA 是一种同时兼容 iOS / Android / Flutter / Web 多个平台的动画格式;

1. 基本简介

    和尚首先赞美一下 SVGA 官网,非常简洁而且主要信息都容易查到,同时看着非常舒服;设计师通过 AE 等工具设计生成好 SVGA 动画后,可直接交付给开发同学通过 SVGAPlayer 直接调用即可,集成和应用都很简单;


    SVGA 提供了在线动画素材预览以及素材元素拆分,还可以将 SVGA 动画转化为 SVG 矢量图元素,非常灵活方便;




2. 案例尝试

    SVGA 提供了多种方式完整的集成方案,和尚简单尝试一下 Flutter 版本应用;

1. 集成 svgaplayer_flutter

    与所有插件使用相同,和尚引入对应版本的 svgaplayer_flutter;目前 svgaplayer_flutter 已支持 Flutter 2.0 空安全;


svgaplayer_flutter: ^2.1.2
复制代码

2. 应用播放 SVGA

2.1 SVGASimpleImage 加载动画

    svgaplayer_flutter 支持播放本地动画和网络线上动画,与 Image 加载本地和网络图片类似;SVGA 提供了封装好 SVGAAnimationController 控制器的 SVGASimpleImage;根据文件类型,通过不同参数进行展示,默认动画效果为重复播放;


class SVGASimpleImage extends StatefulWidget {  final String resUrl;  final String assetsName;  final File file;
SVGASimpleImage({Key key, this.resUrl, this.assetsName, this.file}) : super(key: key);
@override State<StatefulWidget> createState() => _SVGASimpleImageState();}
复制代码


    简单分析源码可得,SVGASimpleImage 根据传递的不同动画路径进行不同方式的展示,通过 SVGAParser.shared 加载和解码不同类型的网络资源、本地资源以及 File 资源等;


class _SVGAPageState extends State<SVGAPage> {  @override  Widget build(BuildContext context) {    return Scaffold(        appBar: AppBar(title: Text('SVGA Page')),        body: Column(children: [          _itemSVGA01(false, 'images/posche.svga'),          _itemSVGA01(true, 'https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true')        ]));  }
_itemSVGA01(isUrl, svgaUrl) { return Expanded( flex: 1, child: SVGASimpleImage(assetsName: isUrl ? null : svgaUrl, resUrl: isUrl ? svgaUrl : null)); }}
复制代码


2.2 SVGAImage & SVGAAnimationController

    SVGASimpleImage 是将 SVGAImageSVGAAnimationController 封装好的动画播放器,若我们想自由控制动画的播放、暂停、重播等操作的话,需要配合 SVGAAnimationController 控制器调节动画的播放过程;


SVGAImage(    this._controller, {    this.fit = BoxFit.contain,    this.clearsAfterStop = true,})
复制代码


    简单分析源码可得,SVGAImage 主要是通过 SVGAAnimationController 来进行动画播放;与图片类似,可以通过 BoxFit 设置动画的布局样式;


    SVGAAnimationController 是对 AnimationController 进一层封装与应用,调用的方法和状态回调基本是一致的;


enum AnimationStatus {  /// 动画开始时结束  dismissed,
/// 动画开始 forward,
/// 逆向动画 reverse,
/// 动画完成结束 completed,}
this.animationController ?.addStatusListener((status) => print('---status---$status'));
复制代码


    SVGAAnimationController 提供了常用的播放方法,和尚简单尝试几种常用的;


  • reset 动画重置;

  • forward 动画播放,和尚建议若动画从头开始播放先调用 reset 使动画重置,防止其他操作影响动画起始位置;

  • stop 动画停止,与 Lottie 动画不同,SVGAAnimationController 没有提供对应的暂停方法,和尚将 stop 理解为暂停和停止,若继续播放则调用 forward 即可;

  • reverse 动画反转,即反向播放动画;

  • repeat 动画重复;

  • fling 使用临界阻尼弹簧和初始速度驱动动画;和尚简单理解在正向播放时,fling 会按起始速度播放完成;


@overridevoid initState() {  super.initState();  this.animationController = SVGAAnimationController(vsync: this)    ..addListener(() {      if (mounted) { setState(() {}); }    });  this._loadAnimation();}
@overridevoid dispose() { this.animationController?.clear(); this.animationController?.dispose(); this.animationController = null; super.dispose();}
void _loadAnimation() async { final videoItem = await _loadSVGA(false, 'images/posche.svga'); if (mounted) setState(() { this.isLoading = false; this.animationController?.videoItem = videoItem; this.animationController ?.addStatusListener((status) => print('---status---$status')); });}
Widget _itemBtn(str) => Expanded( child: Container( margin: EdgeInsets.all(1.0), child: FlatButton( color: Colors.lightBlueAccent, child: Text(str), onPressed: () { if (str == 'start') { animationController?.reset(); animationController?.forward(); } else if (str == 'reverse') { animationController?.reverse(); } else if (str == 'repeat') { animationController?.repeat(); } else if (str == 'resume') { animationController?.forward(); } else if (str == 'stop') { animationController?.stop(); } else if (str == 'fling') { animationController?.fling(); } setState(() {}); })));
复制代码


小结

    Flare 动画是 Google 力荐的一种动画模式,对于复杂动画或游戏处理,快速而高效,和尚测试内存状况良好;


    SVGA 是将 SVGA 矢量图逐帧绘制,通过设置帧率,来生成一个配置文件,使得每一帧都有一个配置,每一帧都是关键帧,通过帧率去刷每一帧的画面,这个思路跟 GIF 很像,但是通过配置使得动画过程中图片都可以得到复用;


     Lottie 动画是逐层绘制,将所有的动画拆成多个层级,每个层级 layer 都有一个动画配置,播放时解析多 0layer 的配置,并给每个 layer 做相应的动画;


    这三种动画模式都非常成熟且应用范围广泛,可以进行实际项目进行具体的选型,和尚对三种动画的研究还不够深入,如又错误,请多多指导!

【签约作者第二季】


来源: 阿策小和尚

发布于: 2021 年 12 月 05 日阅读数: 25
用户头像

还未添加个人签名 2021.05.13 加入

Android / Flutter 小菜鸟~

评论

发布
暂无评论
Flutter 浅尝 Flare / Lottie / SVGA 多种动画模式