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

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

前言

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

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

另外, rive 是经过导出矢量的动画数据文件(也能够包括一些静态资源),然后运用渠道的 Canvas 来完成动画作用,所以它的资源占用体积也不会很大。

当然,rive 其实并不是只针对 Flutter, rive 现在也是全渠道支撑, Android、 iOS、Web、Desktop、Flutter 、React、Vue、C++ 等等都在支撑规模之内。

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

关于 rive 的规划端的简略运用,能够看我之前的 《给 Logo 快速添加动画作用》 ,其实关于程序员来说,rive 其实很好上手,翻开一个 WebEdit 就能够编辑调整。

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

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'),

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

当然,除了上面的 asset ,还能够经过 file 还有 network 等方法这加载,这也算是比较惯例的集成方法。

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

那么运用 rive ,作为开发者端,需求简略知道的几个概念:

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

如下代码所示,一般状况下咱们不需求关怀上述设定,由于只要在规划时考虑好默许状况,那么只需求简略引入就能够播映动画。

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

可是假如你需求更灵敏的操控时,就需求理解上述这些设定的作用,后续才干和动画规划师进行有效的交流和对接

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

  • 知道了画布称号,就能够经过 artboard 切换画布
  • 知道动画称号,就能够经过 animations 指定动画
  • 知道了状况机称号,就能够经过 stateMachines 切换状况机
  • 知道了状况条件,就能够经过 findInput 来切换条件变量

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

animations

咱们先看 animations ,默许状况下 33333.riv 这个 riv 动画播映的是 Shaking 作用,从上图左下角能够看到 Shaking 是一个有循环♻️标识的动画,所以如下图所示车辆动画处于都懂状况。

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

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

接着咱们更新代码,添加了 animations 选择播映 "Jump" ,能够看到,车辆播映到了 Jump 作用的动画,并停留不动,由于 Jump 不是循环动画,所以只会播映一次,然后能够看到 Shaking 也没有了,由于咱们只选中了 Jump

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

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

相同,假如咱们多选中一个 Wheel 动画,能够看到车轮开端动起来,由于 Wheel 也是一个循环♻️动画,所以车轮能够一直滚动。

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

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

所以经过 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"
  ], 

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

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

那装备 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 类型条件。

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

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

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

另外还有 SMITrigger 类型, SMITrigger 只需求经过 fireadvance 去操控动画的前后切换,改动也只能单途径形式一个一个切换。

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

回到开端的设定里,经过 _skin?.change(!_skin!.value); 切换 Bool 值的改动,能够看到此刻车辆开端在 Jump 和 Down 进行改动,这便是最简略的状况机和 Input 的示例作用。

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

当然,如下图变高变胖的人便是经过 SMINumber 随意切换状况的作用,而小黑人换皮肤,便是经过 SMITrigger 单途径形式一个一个切换的动画作用。

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

当然,动画里也可能会包括多个不同类型的 Input ,你能够经过 StateMachineControllerInputs 参数去获取所有你需求的 Input 去操控动画作用。

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

其他

布局调整

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

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

这儿会用到 fitalignment ,他们都是 Flutter 里常见的装备支撑,这儿就不多赘述,默许状况下是 BoxFit.ContainAlignment.Center

文本支撑

新版的 rive 支撑运转过程中替换动画文件里的文本内容,条件是运用新版导出,然后需求编辑器中手动设置称号的文本才干支撑该才能

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

代码上简略说来说,便是在 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);
  @override
  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;
  @override
  void initState() {
    super.initState();
    _controller = SimpleAnimation('idle');
  }
  @override
  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 来操控动画的暂停或许播映。

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

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

/// Demonstrates playing a one-shot animation on demand
class PlayOneShotAnimation extends StatefulWidget {
  const PlayOneShotAnimation({Key? key}) : super(key: key);
  @override
  State<PlayOneShotAnimation> createState() => _PlayOneShotAnimationState();
}
class _PlayOneShotAnimationState extends State<PlayOneShotAnimation> {
  /// Controller for playback
  late RiveAnimationController _controller;
  /// Is the animation currently playing?
  bool _isPlaying = false;
  @override
  void initState() {
    super.initState();
    _controller = OneShotAnimation(
      'bounce',
      autoplay: false,
      onStop: () => setState(() => _isPlaying = false),
      onStart: () => setState(() => _isPlaying = true),
    );
  }
  @override
  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 首要便是用在类似的一次性动画场景上,

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

最终

能够看到 Rive 的运用其实很简略,可是由于状况机的完成,它又能够很灵敏地去操控不同动画的作用。

一个 riv 文件内能够包括多个画板,画板里能够包括多个动画,多个状况机和输入条件,从而完成多样化的动画作用,乃至完成 Rive 版别的 Flutter 小游戏场景。

并且 Rive 并不仅仅支撑 Flutter ,它如今几乎支撑所有你能想到的渠道,那么这样的一个优异的渠道有什么缺陷呢?

那便是 Rive 最近开端收费了,彻底的商业化产品, 其实不给钱你也能够用,仅仅 Free 形式下现已不是以前那个眉清目秀的 Rive 了

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

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

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

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

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

答案是能够,可是本钱无疑巨大,由于 Rive 的门槛不在于它开源的端侧 SDK ,而是在于规划端和产出端,目前的水印是在导出时强制加上的,所以关于运用 Rive 的用户来说,自己搭一套明显不现实。

那么,最终,你会愿意为这样一套产品而付费吗?横竖我是现已付费ing了。