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

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    动画在应用中是非常常见的,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flutter","attrs":{}},{"type":"text","text":" 不仅提供了多种 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Animated","attrs":{}},{"type":"text","text":" 相关的动画模式,还支持多种三方的动画模式,和尚今天尝试一下 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare / Lottie / SVGA","attrs":{}},{"type":"text","text":" 三种动画模式;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Flare","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flutter","attrs":{}},{"type":"text","text":" 的动画插件名称,全名是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"flare_flutter","attrs":{}},{"type":"text","text":";","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 可以为 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"App","attrs":{}},{"type":"text","text":"/游戏/网页等制作酷炫的矢量动画模型;","attrs":{}},{"type":"link","attrs":{"href":"https://rive.app/about-rive/","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Rive","attrs":{}}]},{"type":"text","text":" 是制作 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 动画的网站,在这里不仅可以自由定制动画,还可以免费下载大量开源的优秀动画;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 动画的优势是有效减少文件体积且获取极好的动画效果,适用于与场景交互不大的场景;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 初识 Flare","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    和尚刚开始学习 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":",官网注册账号成功后,便可以访问 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Nima","attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 文件,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 动画包括两种;官网对于不同类型的资源有不同图标区分;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Nima","attrs":{}},{"type":"text","text":" 为较旧格式,仅支持光栅图;主要是为游戏引擎和应用构建 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"2D","attrs":{}},{"type":"text","text":" 动画;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 为较新格式,支持矢量图与光栅图;主要为 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"App","attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Web","attrs":{}},{"type":"text","text":" 构建高效动画,也可用于游戏设计;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/69/69aab85fd94600ba4770ad24ead3ab31.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    对于动画的制作过程,和尚暂不介绍,一是每个人的使用不同,二是和尚也在摸索过程,设计一个满意的动画需要精心设计与调整;资源区分公开和私有版,可根据右下角是否有 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"follow","attrs":{}},{"type":"text","text":" 箭头区分,和尚仅尝试一下开源的动画;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b4/b45f4b013174062919504abed3013d5b.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    和尚选择一个开源的动画进入详情页,可以在 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"open in flutter","attrs":{}},{"type":"text","text":" 中进行自定义调整;可以添加或处理资源样式动画的贝塞尔曲线等,同时根据需求处理是否循环播放,可减少代码中处理;","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e4/e429e394a458c46e4624bd7c30072651.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2a/2a2fcc05707e17b0e65a16380bcffdb9.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 集成方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    和尚尝试的是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 格式的动画,将 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":".flr","attrs":{}},{"type":"text","text":" 动画资源添加到本地资源库 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"images","attrs":{}},{"type":"text","text":" 中;若使用的是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Nima","attrs":{}},{"type":"text","text":" 格式的动画资源,可以尝试 ","attrs":{}},{"type":"link","attrs":{"href":"https://pub.dev/packages/nima","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"nima","attrs":{}}]},{"type":"text","text":" 插件;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. pubspec.yaml 中添加依赖库","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"dependencies:\n flare_flutter: ^1.5.2\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 在文件中添加引用库","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"import \"package:flare_flutter/flare_actor.dart\";\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3. API 调用实现","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    插件中提供了方便的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Widget","attrs":{}},{"type":"text","text":" 可以方便调用;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"FlareActor('images/flare_boll.flr', animation: 'Bounce')\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"分析源码:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"const FlareActor(this.filename,\n {this.boundsNode,\n this.animation,\n this.fit = BoxFit.contain,\n this.alignment = Alignment.center,\n this.isPaused = false,\n this.snapToEnd = false,\n this.controller,\n this.callback,\n this.color,\n this.shouldClip = true});\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"filename","attrs":{}},{"type":"text","text":" 用来加载本地动画资源;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"animation","attrs":{}},{"type":"text","text":" 为制作动画过程中动画名称,且区分大小写,所以建议在编辑动画时动画名称更明确,若没有 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"animation","attrs":{}},{"type":"text","text":" 参数或内容有误,最终展示的时动画的第一帧;","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/53/53ac459c3d8af64c7f9f225edc6f1fa2.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"fit","attrs":{}},{"type":"text","text":" 动画填充样式;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"alignment","attrs":{}},{"type":"text","text":" 动画对齐方式;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"isPause","attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"true","attrs":{}},{"type":"text","text":" 为暂停,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"false","attrs":{}},{"type":"text","text":" 为继续;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"snapToEnd","attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"true","attrs":{}},{"type":"text","text":" 为直接跳到动画最后一帧,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"false","attrs":{}},{"type":"text","text":" 为正常播放;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"color","attrs":{}},{"type":"text","text":" 动画颜色,若整体颜色为纯色可尝试使用,否则会覆盖其他设计颜色;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"controller","attrs":{}},{"type":"text","text":" 控制器,可以通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"controller","attrs":{}},{"type":"text","text":" 控制动画的播放暂停或到具体的动画结点等,灵活方便;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"callback","attrs":{}},{"type":"text","text":" 动画播放完成的回调;当动画设置的是循环播放则无法监听;当动画为非循环模式时,播放完成第一遍后可监听结果;和尚测试若再次 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"play('animation')","attrs":{}},{"type":"text","text":" 时动画会重新播放一次,不会一直循环重复,该监听方法只有一次;","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"class _FlareStatePage extends State {\n FlareControls _controls = FlareControls();\n bool isPause = false, snapToEnd = false;\n\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n body: Column(children: [\n Expanded(\n child: FlareActor('images/flare_question.flr',\n animation: 'default',\n alignment: Alignment.center,\n fit: BoxFit.contain,\n controller: _controls,\n isPaused: isPause,\n snapToEnd: snapToEnd, callback: (name) {\n if (name == 'default') {\n _controls.play('default');\n }\n }),\n flex: 1),\n Row(children: [\n _itemBtn('start'), _itemBtn('pause'), _itemBtn('resume'), _itemBtn('stop')\n ]),\n Expanded(\n child: Column(children: [\n Expanded( child: FlareActor('images/flare_boll.flr', animation: 'Bounce')),\n Expanded( child: FlareActor('images/flare_boll.flr', animation: 'Bounce', color: Colors.orange))\n ]),\n flex: 1)\n ]));\n }\n\n Widget _itemBtn(str) {\n return Expanded(\n child: Container(\n margin: EdgeInsets.all(1.0),\n child: FlatButton(\n color: Colors.lightBlueAccent,\n child: Text(str),\n onPressed: () {\n if (str == 'start') {\n isPause = false;\n snapToEnd = false;\n _controls.play('default');\n } else if (str == 'pause') {\n isPause = true;\n } else if (str == 'resume') {\n isPause = false;\n } else if (str == 'stop') {\n snapToEnd = true;\n }\n setState(() {});\n })));\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/77/77311e1a77dbc50621824d3338d319c4.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Lottie","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"link","attrs":{"href":"http://airbnb.io/lottie/#/","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Lottie","attrs":{}}]},{"type":"text","text":" 在 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Android","attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"iOS","attrs":{}},{"type":"text","text":" 中的应用非常广泛,同时也支持 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flutter","attrs":{}},{"type":"text","text":" 等平台,和尚在官网查询之后发现官网推荐了两个开源的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Lottie","attrs":{}},{"type":"text","text":" 插件,和尚对其中的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"https://github.com/simolus3/fluttie","attrs":{}},{"type":"text","text":" 进行学习尝试;","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d0/d0fa514f20d429e5bd5758db2f97caac.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    和尚首先在 ","attrs":{}},{"type":"link","attrs":{"href":"https://lottiefiles.com","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"lottiefiles","attrs":{}}]},{"type":"text","text":" 中下载了两个酷炫的动画 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"json","attrs":{}},{"type":"text","text":",我们也可以选择合适的动画进行编辑调整;跟原生一样,可以随心设计,当然这项重任还是要交给视觉设计的小姐姐比较好;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/97/97de4c7ba9649af093e3f247bd69611a.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 集成方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    集成方式都是统一的三大步骤;再此之前可以将下载的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"json","attrs":{}},{"type":"text","text":" 文件添加到本地 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"images","attrs":{}},{"type":"text","text":" 资源文件夹中;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. 在 pubspec.yaml 中添加依赖库","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"dependencies:\n fluttie: ^0.3.2\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 在文件中添加引用库","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"import 'package:fluttie/fluttie.dart';\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3. 根据 API 调用","attrs":{}}]},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"loadAnimationFromAsset","attrs":{}},{"type":"text","text":" 异步加载本地 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"json","attrs":{}},{"type":"text","text":" 资源,或通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"loadAnimationFromJson","attrs":{}},{"type":"text","text":" 直接加载 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"json","attrs":{}},{"type":"text","text":" 内容;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"void prepareLottie() async {\n var instance = Fluttie();\n var whaleLottie = await instance.loadAnimationFromAsset('images/animation_demo01.json');\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":2,"normalizeStart":2},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"设置 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"FluttieAnimationController","attrs":{}},{"type":"text","text":" 控制器,绑定动画资源,并设置动画的基本属性;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"prepareAnimation","attrs":{}},{"type":"text","text":" 的固定参数是动画资源,不可缺少;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"repeatCount","attrs":{}},{"type":"text","text":" 可设置动画重复的频率;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RepeatCount.nTimes(n)","attrs":{}},{"type":"text","text":" 重复 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"n+1","attrs":{}},{"type":"text","text":" 次;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RepeatCount.infinite()","attrs":{}},{"type":"text","text":" 无限循环播放;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"RepeatCount.dontRepeat()","attrs":{}},{"type":"text","text":" 仅一次,播放完停止;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"repeatMode","attrs":{}},{"type":"text","text":" 可设置动画播放模式,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"START_OVER","attrs":{}},{"type":"text","text":" 播放完从头再次播放,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"REVERSE","attrs":{}},{"type":"text","text":" 从无到有从有到无;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"duration","attrs":{}},{"type":"text","text":" 可设置动画播放时长;当设置无限重复时不生效;其余根据重复频率使单次动画时长均分;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"preferredSize","attrs":{}},{"type":"text","text":" 可设置动画预加载大小,并不直接控制 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Widget","attrs":{}},{"type":"text","text":" 大小;","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"whaleController = await instance.prepareAnimation(\n whaleLottie,\n repeatCount: const RepeatCount.infinite()\n);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":3,"normalizeStart":3},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"开启动画即可准备好动画的基本要素;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"setState(() { whaleController.start(); });\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":4,"normalizeStart":4},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"将动画绘制在 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Widget","attrs":{}},{"type":"text","text":" 即可初步成功;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"@override\nWidget build(BuildContext context) {\n return Scaffold(\n body: Center(\n child: Column(mainAxisSize: MainAxisSize.min, children: [\n Container( width: 280.0, height: 200.0,\n child: FluttieAnimation(whaleController))\n )\n ])));\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":5,"normalizeStart":5},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"我们也可以动态监听动画状态并进行处理;","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"start()","attrs":{}},{"type":"text","text":" 从头开启动画;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"pause()","attrs":{}},{"type":"text","text":" 暂停动画;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"unpause()","attrs":{}},{"type":"text","text":" 从暂停处继续播放动画;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"stopAndReset()","attrs":{}},{"type":"text","text":" 停止动画,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"rewind","attrs":{}},{"type":"text","text":" 为 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"true","attrs":{}},{"type":"text","text":" 时结束动画并到动画开始时第一帧;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"false","attrs":{}},{"type":"text","text":" 为技术动画并到动画最后一帧;","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"Row(children: [\n Expanded( flex: 1,\n child: FlatButton(\n onPressed: () { starController.start(); },\n child: Text('start'))),\n Expanded( flex: 1,\n child: FlatButton(\n onPressed: () { starController.pause(); },\n child: Text('pause'))),\n Expanded( flex: 1,\n child: FlatButton(\n onPressed: () { starController.unpause(); },\n child: Text('resume'))),\n Expanded( flex: 1,\n child: FlatButton(\n onPressed: () { starController.stopAndReset(rewind: true); },\n child: Text('stop')))\n])\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7f/7fb703cf81317f7d9a9c499082c8138a.jpeg","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 注意事项","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dispose()","attrs":{}},{"type":"text","text":" 动画","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    动画对应用内存占用较大,建议在页面销毁或关闭时将动画销毁;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"@override\nvoid dispose() {\n super.dispose();\n whaleController?.dispose();\n starController?.dispose();\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. dispose() 与 stopAndReset() 区别","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"stopAndReset()","attrs":{}},{"type":"text","text":" 方法用来控制动画的停止状态,资源依然存在内存中,之后可继续操作动画的状态;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"dispose()","attrs":{}},{"type":"text","text":" 方法用来停止动画并释放资源,之后不能再操作动画状态;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"class _LottieStatePage extends State {\n FluttieAnimationController whaleController, starController;\n\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n body: Center(\n child: Column(mainAxisSize: MainAxisSize.min, children: [\n Container(\n width: 280.0,\n height: 200.0,\n child: FluttieAnimation(whaleController)),\n Container(child: FluttieAnimation(starController)),\n Row(children: [\n Expanded(\n flex: 1,\n child: FlatButton(\n onPressed: () {\n starController.start();\n },\n child: Text('start'))),\n Expanded(\n flex: 1,\n child: FlatButton(\n onPressed: () {\n starController.pause();\n },\n child: Text('pause'))),\n Expanded(\n flex: 1,\n child: FlatButton(\n onPressed: () {\n starController.unpause();\n },\n child: Text('resume'))),\n Expanded(\n flex: 1,\n child: FlatButton(\n onPressed: () {\n starController.stopAndReset(rewind: false);\n },\n child: Text('stop')))\n ])\n ])));\n }\n\n @override\n void dispose() {\n super.dispose();\n whaleController?.dispose();\n starController?.dispose();\n }\n\n @override\n void initState() {\n super.initState();\n prepareLottie();\n }\n\n void prepareLottie() async {\n var instance = Fluttie();\n var whaleLottie =\n await instance.loadAnimationFromAsset('images/animation_demo01.json');\n whaleController = await instance.prepareAnimation(\n whaleLottie,\n repeatCount: const RepeatCount.nTimes(2));\n\n var starLottie = await instance.loadAnimationFromAsset('images/star.json');\n starController = await instance.prepareAnimation(starLottie,\n repeatCount: const RepeatCount.infinite(),\n repeatMode: RepeatMode.START_OVER);\n\n setState(() {\n whaleController.start();\n starController.start();\n });\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/3c/3c862fba1ddffb1539ee6a89ee728258.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"SVGA","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    和尚之前尝试了 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Lottie","attrs":{}},{"type":"text","text":" 动画,实现效果都很高效;今天和尚尝试另一种思路 ","attrs":{}},{"type":"link","attrs":{"href":"http://svga.io/index.html","title":"","type":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}}]},{"type":"text","text":" 动画;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 是一种同时兼容 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"iOS / Android / Flutter / Web","attrs":{}},{"type":"text","text":" 多个平台的动画格式;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 基本简介","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    和尚首先赞美一下 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 官网,非常简洁而且主要信息都容易查到,同时看着非常舒服;设计师通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AE","attrs":{}},{"type":"text","text":" 等工具设计生成好 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 动画后,可直接交付给开发同学通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAPlayer","attrs":{}},{"type":"text","text":" 直接调用即可,集成和应用都很简单;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 提供了在线动画素材预览以及素材元素拆分,还可以将 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 动画转化为 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVG","attrs":{}},{"type":"text","text":" 矢量图元素,非常灵活方便;","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/be/be46bb424d249943f446a69d7076ae76.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/46/46728a1dd932114b0c825451ceb7a580.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/db/dbe98f778ed4bc59c166ec48f0f529ab.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 案例尝试","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 提供了多种方式完整的集成方案,和尚简单尝试一下 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flutter","attrs":{}},{"type":"text","text":" 版本应用;","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. 集成 svgaplayer_flutter","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    与所有插件使用相同,和尚引入对应版本的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"svgaplayer_flutter","attrs":{}},{"type":"text","text":";目前 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"svgaplayer_flutter","attrs":{}},{"type":"text","text":" 已支持 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flutter 2.0","attrs":{}},{"type":"text","text":" 空安全;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"svgaplayer_flutter: ^2.1.2\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 应用播放 SVGA","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.1 SVGASimpleImage 加载动画","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"svgaplayer_flutter","attrs":{}},{"type":"text","text":" 支持播放本地动画和网络线上动画,与 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Image","attrs":{}},{"type":"text","text":" 加载本地和网络图片类似;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 提供了封装好 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAAnimationController","attrs":{}},{"type":"text","text":" 控制器的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGASimpleImage","attrs":{}},{"type":"text","text":";根据文件类型,通过不同参数进行展示,默认动画效果为重复播放;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"class SVGASimpleImage extends StatefulWidget {\n final String resUrl;\n final String assetsName;\n final File file;\n\n SVGASimpleImage({Key key, this.resUrl, this.assetsName, this.file}) : super(key: key);\n\n @override\n State createState() => _SVGASimpleImageState();\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    简单分析源码可得,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGASimpleImage","attrs":{}},{"type":"text","text":" 根据传递的不同动画路径进行不同方式的展示,通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAParser.shared","attrs":{}},{"type":"text","text":" 加载和解码不同类型的网络资源、本地资源以及 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"File","attrs":{}},{"type":"text","text":" 资源等;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"class _SVGAPageState extends State {\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(title: Text('SVGA Page')),\n body: Column(children: [\n _itemSVGA01(false, 'images/posche.svga'),\n _itemSVGA01(true, 'https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true')\n ]));\n }\n\n _itemSVGA01(isUrl, svgaUrl) {\n return Expanded( flex: 1,\n child: SVGASimpleImage(assetsName: isUrl ? null : svgaUrl, resUrl: isUrl ? svgaUrl : null));\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e1/e19157db3e9a307ed4cbff43040c29df.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"2.2 SVGAImage & SVGAAnimationController","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGASimpleImage","attrs":{}},{"type":"text","text":" 是将 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAImage","attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAAnimationController","attrs":{}},{"type":"text","text":" 封装好的动画播放器,若我们想自由控制动画的播放、暂停、重播等操作的话,需要配合 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAAnimationController","attrs":{}},{"type":"text","text":" 控制器调节动画的播放过程;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"SVGAImage(\n this._controller, {\n this.fit = BoxFit.contain,\n this.clearsAfterStop = true,\n})\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    简单分析源码可得,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAImage","attrs":{}},{"type":"text","text":" 主要是通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAAnimationController","attrs":{}},{"type":"text","text":" 来进行动画播放;与图片类似,可以通过 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"BoxFit","attrs":{}},{"type":"text","text":" 设置动画的布局样式;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAAnimationController","attrs":{}},{"type":"text","text":" 是对 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"AnimationController","attrs":{}},{"type":"text","text":" 进一层封装与应用,调用的方法和状态回调基本是一致的;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"enum AnimationStatus {\n /// 动画开始时结束\n dismissed,\n\n /// 动画开始\n forward,\n\n /// 逆向动画\n reverse,\n\n /// 动画完成结束\n completed,\n}\n\nthis.animationController ?.addStatusListener((status) => print('---status---$status'));\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAAnimationController","attrs":{}},{"type":"text","text":" 提供了常用的播放方法,和尚简单尝试几种常用的;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"reset","attrs":{}},{"type":"text","text":" 动画重置;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"forward","attrs":{}},{"type":"text","text":" 动画播放,和尚建议若动画从头开始播放先调用 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"reset","attrs":{}},{"type":"text","text":" 使动画重置,防止其他操作影响动画起始位置;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"stop","attrs":{}},{"type":"text","text":" 动画停止,与 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Lottie","attrs":{}},{"type":"text","text":" 动画不同,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGAAnimationController","attrs":{}},{"type":"text","text":" 没有提供对应的暂停方法,和尚将 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"stop","attrs":{}},{"type":"text","text":" 理解为暂停和停止,若继续播放则调用 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"forward","attrs":{}},{"type":"text","text":" 即可;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"reverse","attrs":{}},{"type":"text","text":" 动画反转,即反向播放动画;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"repeat","attrs":{}},{"type":"text","text":" 动画重复;","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"fling","attrs":{}},{"type":"text","text":" 使用临界阻尼弹簧和初始速度驱动动画;和尚简单理解在正向播放时,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"fling","attrs":{}},{"type":"text","text":" 会按起始速度播放完成;","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"@override\nvoid initState() {\n super.initState();\n this.animationController = SVGAAnimationController(vsync: this)\n ..addListener(() {\n if (mounted) { setState(() {}); }\n });\n this._loadAnimation();\n}\n\n@override\nvoid dispose() {\n this.animationController?.clear();\n this.animationController?.dispose();\n this.animationController = null;\n super.dispose();\n}\n\nvoid _loadAnimation() async {\n final videoItem = await _loadSVGA(false, 'images/posche.svga');\n if (mounted)\n setState(() {\n this.isLoading = false;\n this.animationController?.videoItem = videoItem;\n this.animationController ?.addStatusListener((status) => print('---status---$status'));\n });\n}\n\nWidget _itemBtn(str) => Expanded(\n child: Container(\n margin: EdgeInsets.all(1.0),\n child: FlatButton(\n color: Colors.lightBlueAccent,\n child: Text(str),\n onPressed: () {\n if (str == 'start') {\n animationController?.reset();\n animationController?.forward();\n } else if (str == 'reverse') {\n animationController?.reverse();\n } else if (str == 'repeat') {\n animationController?.repeat();\n } else if (str == 'resume') {\n animationController?.forward();\n } else if (str == 'stop') {\n animationController?.stop();\n } else if (str == 'fling') {\n animationController?.fling();\n }\n setState(() {});\n })));\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/30/304719be3c0c5aa7ea918998194b50d7.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"小结","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Flare","attrs":{}},{"type":"text","text":" 动画是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Google","attrs":{}},{"type":"text","text":" 力荐的一种动画模式,对于复杂动画或游戏处理,快速而高效,和尚测试内存状况良好;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 是将 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"SVGA","attrs":{}},{"type":"text","text":" 矢量图逐帧绘制,通过设置帧率,来生成一个配置文件,使得每一帧都有一个配置,每一帧都是关键帧,通过帧率去刷每一帧的画面,这个思路跟 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"GIF","attrs":{}},{"type":"text","text":" 很像,但是通过配置使得动画过程中图片都可以得到复用;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"     ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Lottie","attrs":{}},{"type":"text","text":" 动画是逐层绘制,将所有的动画拆成多个层级,每个层级 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"layer","attrs":{}},{"type":"text","text":" 都有一个动画配置,播放时解析多 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"0","attrs":{}},{"type":"text","text":" 个 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"layer","attrs":{}},{"type":"text","text":" 的配置,并给每个 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"layer","attrs":{}},{"type":"text","text":" 做相应的动画;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    这三种动画模式都非常成熟且应用范围广泛,可以进行实际项目进行具体的选型,和尚对三种动画的研究还不够深入,如又错误,请多多指导!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":1,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【签约作者第二季】","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"来源: 阿策小和尚","attrs":{}}]}],"attrs":{}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章