Flutter 最优秀动画库「完全商业化」,Rive 2 你全面了解过吗?

2024-02-08 00:40

本文主要是介绍Flutter 最优秀动画库「完全商业化」,Rive 2 你全面了解过吗?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

说到 rive ,非 Flutter 开发者可能会感觉比较陌生,而做过 Flutter 开发的可能对 rive 会有所耳闻,因为 rive 在一开始叫 flare ,是 2dimensions 公司的开源动画产品,在发布之初由于和 Flutter 团队有深入合作,所以在初期一直是 Flutter 官方推荐的动画框架之一。

前言

rive 作为一个面向设计师的动画框架,他支持在 Web Editor 里进行 UI 编排和动画绘制,当然现在他也支持 PC 客户端开发,整体开发环境需求上相对 Lottie 会轻量化很多。

另外, rive 是通过导出矢量的动画数据文件(也可以包含一些静态资源),然后利用平台的 Canvas 来实现动画效果,所以它的资源占用体积也不会很大。

当然,rive 其实并不是只针对 Flutter, rive 现在也是全平台支持, Android、 iOS、Web、Desktop、Flutter 、React、Vue、C++ 等等都在支持范围之内。

关于 rive 的设计端的简单使用,可以看我之前的 《给掘金 Logo 快速添加动画效果》 ,其实对于程序员来说,rive 其实很好上手,打开一个 WebEdit 就可以编辑调整。

PS,第二代 rive 和第一代 flare 存在断档不兼容,而且基本可以忽略迁移的可能,当然, flare 和 rive 其实可以同时存在一个项目不会冲突,所以也不需要当心旧动画的升级问题

Rive Flutter

开始进入主题,其实 rive 比 flare 使用起来更加简单,如下代码所示,只需要通过 RiveAnimation.asset 就可以实现一个下图里炫酷的动画效果,

dependencies:rive: 0.9.0import 'package:rive/rive.dart';
RiveAnimation.asset('static/file/launch.riv'),

当然,除了上面的 asset ,还可以通过 file 还有 network 等方式这加载,这也算是比较常规的集成方式。

那么使用 rive ,作为开发者端,需要简单知道的几个概念:

  • Artboards:画布,rive 里至少会有一块画布,当然一个 riv 动画文件可以有多个画布
  • animations:需要播放的动画
  • StateMachine:状态机,可以将动画连接在一起并定义切换条件的支持
  • Inputs:StateMachine 的输入,然后可用于与 StateMachine 交互并修改动画切换的状态

如下代码所示,一般情况下我们不需要关心上述设定,因为只要在设计时考虑好默认情况,那么只需要简单引入就可以播放动画。

RiveAnimation.asset('assets/33333.riv')

但是如果你需要更灵活的控制时,就需要理解上述这些设定的作用,后续才能和动画设计师进行有效的沟通和对接

如下图所示就是对应的设定解读,例如:

  • 知道了画布名称,就可以通过 artboard 切换画布
  • 知道动画名称,就可以通过 animations 指定动画
  • 知道了状态机名称,就可以通过 stateMachines 切换状态机
  • 知道了状态条件,就可以通过 findInput 来切换条件变量

animations

我们先看 animations ,默认情况下 33333.riv 这个 riv 动画播放的是 Shaking 效果,从上图左下角可以看到 Shaking 是一个有循环♻️标识的动画,所以如下图所示车辆动画处于都懂状态。

RiveAnimation.asset('assets/33333.riv')

接着我们更新代码,添加了 animations 选择播放 "Jump" ,可以看到,车辆播放到了 Jump 效果的动画,并停留不动,因为 Jump 不是循环动画,所以只会播放一次,然后可以看到 Shaking 也没有了,因为我们只选中了 Jump

RiveAnimation.asset('assets/33333.riv',animations: ["Jump",],
)

同样,如果我们多选中一个 Wheel 动画,可以看到车轮开始动起来,因为 Wheel 也是一个循环♻️动画,所以车轮可以一直滚动。

RiveAnimation.asset('assets/33333.riv',animations: ["Jump","Wheel",],
)

所以通过 animations 我们可以快捷组合需要播放的动画效果。

stateMachines & Inputs

前面我们知道了可以通过 animations 配置动画,那么接下来再看看如何通过 stateMachines 来控制动画效果。

animations 一样,stateMachines 同样是一个List<String>,也就是可以配置多个状态,例如通过前面编辑器我们知道,此时 33333.riv 的状态机只有一个 State Machine 1 ,所以我们只需要配置上对应的 stateMachines ,就可以看到此时车辆动起来,进入状态机动画模式,也即是 Entry

RiveAnimation.asset('assets/33333.riv',stateMachines: ["State Machine 1"], 

那配置 stateMachines 只是进入 Entry,如果要控制状态变化该怎么办?这就要说到 Inputs

获取 Inputs 我们需要在 _onRiveInit 回调里去获取,如下代码所示:

  • 首先通过 StateMachineController.fromArtboard 获取到状态机的控制器,这样我们使用的是默认画板,所以直接使用初始化时传入的 artboard 即可
  • fromArtboard 时通过 State Machine 1 指定了状态机,然后通过onStateChange 监听状态机变化
  • 通过 addController 将获取到的状态机控制器添加到画布
  • 通过 findInput 找到对应的控制状态 SMIBool
  • 调用 change 改变 SMIBool 的 value 来切换动画状态
RiveAnimation.asset('assets/33333.riv',onInit: _onRiveInit,
)SMIBool? _skin;
void _onRiveInit(Artboard artboard) {final controller = StateMachineController.fromArtboard(artboard,'State Machine 1',onStateChange: _onStateChange,);artboard.addController(controller!);_skin = controller.findInput<bool>('Boolean 1') as SMIBool;
}void _onStateChange(String stateMachineName, String stateName) {print("stateMachineName $stateMachineName stateName $stateName");
}void _swapSkin() {_skin?.change(!_skin!.value);
}

为什么这里是 SMIBool ? 因为在该状态机设定里用的是 Bool 类型条件。

当然,除了 Bool 还可以用数字作为判断条件,对应的 Type 类型也会变成 SMINumber

另外还有 SMITrigger 类型, SMITrigger 只需要通过 fireadvance 去控制动画的前后切换,变化也只能单路径模式一个一个切换。

回到最初的设定里,通过 _skin?.change(!_skin!.value); 切换 Bool 值的变化,可以看到此时车辆开始在 Jump 和 Down 进行变化,这就是最简单的状态机和 Input 的示例效果。

当然,如下图变高变胖的人就是通过 SMINumber 随意切换状态的效果,而小黑人换皮肤,就是通过 SMITrigger 单路径模式一个一个切换的动画效果。

777777-2

当然,动画里也可能会包含多个不同类型的 Input ,你可以通过 StateMachineControllerInputs 参数去获取所有你需要的 Input 去控制动画效果。

其他

布局调整

其实了解上面哪些,大致上你就基本学会完美使用 rive 了,剩下的一些参数支持就都是小事,例如:

RiveAnimation.network('https://cdn.rive.app/animations/vehicles.riv',fit: BoxFit.fitWidth,alignment: Alignment.topCenter,
);

这里会用到 fitalignment ,他们都是 Flutter 里常见的配置支持,这里就不多赘述,默认情况下是 BoxFit.ContainAlignment.Center

文本支持

新版的 rive 支持运行过程中替换动画文件里的文本内容,前提是使用新版导出,然后需要编辑器中手动设置名称的文本才能支持该能力

代码上简单说来说,就是在 onInit 的时候通过自定义的文本名称,然后通过 artboard 获取该节点,从而修改文本内容。

extension _TextExtension on Artboard {TextValueRun? textRun(String name) => component<TextValueRun>(name);
}RiveAnimation.asset('assets/hello_world_text.riv',animations: const ['Timeline 1'],onInit: (artboard) {final textRun = artboard.textRun('MyRun')!; // find text run named "MyRun"print('Run text used to be ${textRun.text}');textRun.text = 'Hi Flutter Runtime!';},)

播放控制

现在的 rive 自带的 RiveAnimationController 对比 flare 弱化了很多,基本上就是用来实现简单的 playpause stop 等,默认官方提供了 SimpleAnimationOneShotAnimation 两种 RiveAnimationController 默认实现。

一般用不上自定义。

SimpleAnimation 主要是提供单个动画的简单播放控制,如 play、 pause (isActive) 和 reset ,以下是官方 Demo 的示例,

class PlayPauseAnimation extends StatefulWidget {const PlayPauseAnimation({Key? key}) : super(key: key);State<PlayPauseAnimation> createState() => _PlayPauseAnimationState();
}class _PlayPauseAnimationState extends State<PlayPauseAnimation> {/// Controller for playbacklate RiveAnimationController _controller;/// Toggles between play and pause animation statesvoid _togglePlay() =>setState(() => _controller.isActive = !_controller.isActive);/// Tracks if the animation is playing by whether controller is runningbool get isPlaying => _controller.isActive;void initState() {super.initState();_controller = SimpleAnimation('idle');}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Animation Example'),),body: RiveAnimation.asset('assets/off_road_car.riv',fit: BoxFit.cover,controllers: [_controller],// Update the play state when the widget's initializedonInit: (_) => setState(() {}),),floatingActionButton: FloatingActionButton(onPressed: _togglePlay,tooltip: isPlaying ? 'Pause' : 'Play',child: Icon(isPlaying ? Icons.pause : Icons.play_arrow,),),);}
}

主要就是通过 isActive 来控制动画的暂停或者播放。

OneShotAnimation 主要用于在播放完一次动画后自动停止并重置动画,以下是官方 Demo 的示例,其实 OneShotAnimation 就是继承了 SimpleAnimation ,然后在其基础上增加了监听,在播放结束时调用 reset 重制动画而已。

/// Demonstrates playing a one-shot animation on demand
class PlayOneShotAnimation extends StatefulWidget {const PlayOneShotAnimation({Key? key}) : super(key: key);State<PlayOneShotAnimation> createState() => _PlayOneShotAnimationState();
}class _PlayOneShotAnimationState extends State<PlayOneShotAnimation> {/// Controller for playbacklate RiveAnimationController _controller;/// Is the animation currently playing?bool _isPlaying = false;void initState() {super.initState();_controller = OneShotAnimation('bounce',autoplay: false,onStop: () => setState(() => _isPlaying = false),onStart: () => setState(() => _isPlaying = true),);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('One-Shot Example'),),body: Center(child: RiveAnimation.asset('assets/vehicles.riv',animations: const ['idle', 'curves'],fit: BoxFit.cover,controllers: [_controller],),),floatingActionButton: FloatingActionButton(onPressed: () => _isPlaying ? null : _controller.isActive = true,tooltip: 'Bounce',child: const Icon(Icons.arrow_upward),),);}
}

上述代码就是在行驶过程中,点击是触发 'bounce' 的一次性跳跃效果,OneShotAnimation 主要就是用在类似的一次性动画场景上,

最后

可以看到 Rive 的使用其实很简单,但是因为状态机的实现,它又可以很灵活地去控制不同动画的效果。

一个 riv 文件内可以包含多个画板,画板里可以包含多个动画,多个状态机和输入条件,从而实现多样化的动画效果,甚至实现 Rive 版本的 Flutter 小游戏场景。

而且 Rive 并不只是支持 Flutter ,它如今几乎支持所有你能想到的平台,那么这样的一个优秀的平台有什么缺点呢?

那就是 Rive 最近开始收费了,完全的商业化产品, 其实不给钱你也可以用,只是 Free 模式下已经不是以前那个眉清目秀的 Rive 了

Free 模式的 Rive 会有多个如下图所示的 Make with Rive 的水印,同时现在 Free 模式不支持 Share links 了,也就是你自己体验一下,要投入生产使用还是得付费。

那么有机智的小伙伴可能就要说了, Rive 不是开源的吗?那我们可以自己弄一套免费的吗?

答案是可以,但是成本无疑巨大,因为 Rive 的门槛不在于它开源的端侧 SDK ,而是在于设计端和产出端,目前的水印是在导出时强制加上的,所以对于使用 Rive 的用户来说,自己搭一套明显不现实。

那么,最后,你会愿意为这样一套产品而付费吗?反正我是已经付费ing了。

这篇关于Flutter 最优秀动画库「完全商业化」,Rive 2 你全面了解过吗?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/689441

相关文章

全网最全Tomcat完全卸载重装教程小结

《全网最全Tomcat完全卸载重装教程小结》windows系统卸载Tomcat重新通过ZIP方式安装Tomcat,优点是灵活可控,适合开发者自定义配置,手动配置环境变量后,可通过命令行快速启动和管理... 目录一、完全卸载Tomcat1. 停止Tomcat服务2. 通过控制面板卸载3. 手动删除残留文件4.

Java JUC并发集合详解之线程安全容器完全攻略

《JavaJUC并发集合详解之线程安全容器完全攻略》Java通过java.util.concurrent(JUC)包提供了一整套线程安全的并发容器,它们不仅是简单的同步包装,更是基于精妙并发算法构建... 目录一、为什么需要JUC并发集合?二、核心并发集合分类与详解三、选型指南:如何选择合适的并发容器?在多

Python实现精确小数计算的完全指南

《Python实现精确小数计算的完全指南》在金融计算、科学实验和工程领域,浮点数精度问题一直是开发者面临的重大挑战,本文将深入解析Python精确小数计算技术体系,感兴趣的小伙伴可以了解一下... 目录引言:小数精度问题的核心挑战一、浮点数精度问题分析1.1 浮点数精度陷阱1.2 浮点数误差来源二、基础解决

从入门到精通详解Python虚拟环境完全指南

《从入门到精通详解Python虚拟环境完全指南》Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧... 目录什么是python虚拟环境一、使用venv创建和管理虚拟环境1.1 创建虚拟环境1.2 激活虚拟环境1.3 验证虚

从基础到高级详解Python数值格式化输出的完全指南

《从基础到高级详解Python数值格式化输出的完全指南》在数据分析、金融计算和科学报告领域,数值格式化是提升可读性和专业性的关键技术,本文将深入解析Python中数值格式化输出的相关方法,感兴趣的小伙... 目录引言:数值格式化的核心价值一、基础格式化方法1.1 三种核心格式化方式对比1.2 基础格式化示例

Python ORM神器之SQLAlchemy基本使用完全指南

《PythonORM神器之SQLAlchemy基本使用完全指南》SQLAlchemy是Python主流ORM框架,通过对象化方式简化数据库操作,支持多数据库,提供引擎、会话、模型等核心组件,实现事务... 目录一、什么是SQLAlchemy?二、安装SQLAlchemy三、核心概念1. Engine(引擎)

MySQL 数据库表操作完全指南:创建、读取、更新与删除实战

《MySQL数据库表操作完全指南:创建、读取、更新与删除实战》本文系统讲解MySQL表的增删查改(CURD)操作,涵盖创建、更新、查询、删除及插入查询结果,也是贯穿各类项目开发全流程的基础数据交互原... 目录mysql系列前言一、Create(创建)并插入数据1.1 单行数据 + 全列插入1.2 多行数据

Python 字符串裁切与提取全面且实用的解决方案

《Python字符串裁切与提取全面且实用的解决方案》本文梳理了Python字符串处理方法,涵盖基础切片、split/partition分割、正则匹配及结构化数据解析(如BeautifulSoup、j... 目录python 字符串裁切与提取的完整指南 基础切片方法1. 使用切片操作符[start:end]2

SpringBoot加载profile全面解析

《SpringBoot加载profile全面解析》SpringBoot的Profile机制通过多配置文件和注解实现环境隔离,支持开发、测试、生产等不同环境的灵活配置切换,无需修改代码,关键点包括配置文... 目录题目详细答案什么是 Profile配置 Profile使用application-{profil

Python自定义异常的全面指南(入门到实践)

《Python自定义异常的全面指南(入门到实践)》想象你正在开发一个银行系统,用户转账时余额不足,如果直接抛出ValueError,调用方很难区分是金额格式错误还是余额不足,这正是Python自定义异... 目录引言:为什么需要自定义异常一、异常基础:先搞懂python的异常体系1.1 异常是什么?1.2