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

news2024/11/26 16:35:58

说到 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.0
    
import '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 playback
  late RiveAnimationController _controller;

  /// Toggles between play and pause animation states
  void _togglePlay() =>
      setState(() => _controller.isActive = !_controller.isActive);

  /// Tracks if the animation is playing by whether controller is running
  bool 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 initialized
        onInit: (_) => 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 playback
  late 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了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/978528.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

golang获取prometheus数据(prometheus/client_golang包)

文章目录 1. 创建链接1.1 语法1.2 完整示例 2. 简单查询2.1 语法2.2 完整示例 3. 范围值查询3.1 语法3.2 完整示例 【附官方示例】 1. 创建链接 1.1 语法 语法 func NewClient(cfg Config) (Client, error)结构体 type Config struct {Address stringClient *ht…

16-数据结构-图的存储结构

简介&#xff1a;主要为图的顺序存储和链式存储。其中顺序存储即邻接矩阵的画法以及代码&#xff0c;邻接矩阵又分为有权图和无权图&#xff0c;区别就是有数据的地方填权值&#xff0c;无数据的地方可以填0或者∞&#xff0c;而有权图和无权图&#xff0c;又细分为有向图和无向…

阿里云服务器退款政策及退款流程解析

阿里云服务器如何退款&#xff1f;云服务器在哪申请退款&#xff1f;在用户中心订单管理中的退订管理中退款&#xff0c;阿里云百科分享阿里云服务器退款流程&#xff0c;包括申请退款入口、云服务器退款限制条件、退款多久到账等详细说明&#xff1a; 目录 阿里云服务器退款…

FPGA GTH 全网最细讲解,aurora 8b/10b协议,HDMI板对板视频传输,提供2套工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、GTH 全网最细解读GTH 基本结构GTH 发送和接收处理流程GTH 的参考时钟GTH 发送接口GTH 接收接口GTH IP核调用和使用 4、设计思路框架视频源选择silicon9011解码芯片配置及采集动态彩条视频数据组包GTH aurora 8b/10…

【Java 基础篇】深入理解 Java 内部类:嵌套在嵌套中的编程奇妙世界

在 Java 编程中&#xff0c;内部类&#xff08;Inner Class&#xff09;是一个非常强大且灵活的概念&#xff0c;它允许在一个类的内部定义另一个类。内部类可以访问外部类的成员&#xff0c;包括私有成员&#xff0c;这使得内部类在许多编程场景中都非常有用。本篇博客将详细介…

js如何实现数组去重的常用方法

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Set&#xff08;ES6&#xff09;⭐ 使用 filter 和 indexOf⭐ 使用 reduce⭐ 使用对象属性⭐ 使用 includes 方法&#xff08;ES6&#xff09;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方…

$ref属性的介绍与使用

在Vue.js中&#xff0c;$ref是一个特殊的属性&#xff0c;用于访问Vue组件中的DOM元素或子组件实例。它允许你直接访问组件内部的DOM元素或子组件&#xff0c;并且可以在需要时进行操作或修改。以下是有关$ref的详细介绍和示例演示&#xff0c;给大家做一个简单的介绍和概念区分…

库中是如何实现string类的?

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

如何查看B站UP主数据?如何看懂B站数据?

bilibili是独特且稀缺的PUGC视频社区&#xff0c;拥有浓厚社区氛围的视频社区。有别于短视频&#xff0c;PUGC视频创作门槛高&#xff0c;视频内容更充实&#xff0c;bilibili是PUGC视频行业的领跑者&#xff0c;同时&#xff0c;bilibili拥有社区产品特有的高创作渗透率和高互…

SQL SERVER 如何实现UNDO REDO 和PostgreSQL 有近亲关系吗

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,SQL Server&#xff0c;Redis &#xff0c;Oracle ,Oceanbase 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请加微信号 l…

分类任务评价指标

分类任务评价指标 分类任务中&#xff0c;有以下几个常用指标&#xff1a; 混淆矩阵准确率&#xff08;Accuracy&#xff09;精确率&#xff08;查准率&#xff0c;Precision&#xff09;召回率&#xff08;查全率&#xff0c;Recall&#xff09;F-scorePR曲线ROC曲线 1. 混…

配置Jenkins

主要是配置Jenkins和jdk,maven的插件

Spring Cloud Alibaba Nacos配置导入问题解决方案

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

论文复现--lightweight-human-pose-estimation-3d-demo.pytorch(单视角多人3D实时动作捕捉DEMO)

分类&#xff1a;动作捕捉 github地址&#xff1a;https://github.com/Daniil-Osokin/lightweight-human-pose-estimation-3d-demo.pytorch 所需环境&#xff1a; Windows10&#xff0c;conda 4.13.0&#xff1b; 目录 conda环境配置安装Pytorch全家桶安装TensorRT&#xff08;…

[数据集][目标检测]裸土识别裸土未覆盖目标检测数据集VOC格式857张2类别

数据集格式&#xff1a;Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件&#xff0c;仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数)&#xff1a;857 标注数量(xml文件个数)&#xff1a;857 标注类别数&#xff1a;2 标注类别名称:["luotu","n…

Python网络爬虫中这七个li标签下面的属性值,不是固定的,怎样才能拿到他们的值呢?...

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 愚以为宫中之事&#xff0c;事无大小&#xff0c;悉以咨之&#xff0c;然后施行&#xff0c;必能裨补阙漏&#xff0c;有所广益。 大家好&#xff0c;我…

Java8实战-总结21

Java8实战-总结21 使用流归约元素求和无初始值 最大值和最小值 使用流 归约 到目前为止&#xff0c;见到过的终端操作都是返回一个boolean(allMatch之类的)、void(forEach)或optional对象(findAny等)。也见过了使用collect来将流中的所有元素组合成一个List。 如何把一个流中…

r7 7840u和r7 7840hs差距 锐龙r77840u和r77840hs对比

锐龙7 7840U 采用Zen3架构、8核心16线程&#xff0c;基准频率疑似3.3GHz&#xff0c;同样集成RDNA3架构核显Radeon 780M&#xff0c;也是12个CU单元 r7 7840U 的处理器在 Cinebench R23 中多核跑分 14825 分 选r7 7840u还是 R7 7840HS这些点很重要 http://www.adiannao.cn/dy …

小红书笔记爬虫

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

codesys可视化

可视化有2种&#xff1a;本地和网页 触摸屏的话&#xff0c;属于网页。 1先配置IDE 如果有些控件&#xff0c;别人有&#xff0c;而你却没有&#xff0c;原因是&#xff1a;你库里没有引用。 比如缺少3D轨迹的控制面板&#xff0c;你需要库内引用 VisuStruct3DControl编译报错…