感觉官方的案例对我这种level的开发者来说感觉的真的很晦涩,也没什么像样的中文文档,所以做了些尝试,大致理解了一些基本的使用方式。有些较为复杂的素材琢磨不明白,然后把自己所知的做一个总结吧。

首先是通过RiveAnimation组件加载的三种方式,分别可以通过asset资源文件、网络url和本地文件进行动画加载。

以及通过RiveAnimationController控制动画的暂停与播放。

还有一种加载方法是通过直接加载文件并解析转化成RivaFile类型,获取一个所谓artboard的画板,然后通过这个画板获取一个StateMachineController的状态器,接下来在状态其中通过findSMI和findInput方法获取到可执行的动画。动画的名称通过rive网站的editor打开自己查看,这里接似懂非懂了,见得的动画基本按照编辑器里的名称来就可以,有些复杂类型的小游戏实在是没明白怎么加载,按常规动画那套搞我没搞出来。有明白的可以指教下。主要还是findSMI可以找到SMITrigger这种类型的动画控制器,过过调用其实例的fire方法执行动画,还有findInput方法获得的SMIInput实例,通过传入泛型,double、bool等控制数值发生变化来触动动画的执行。具体的可以看下下面的一些案例,效果和代码都贴出来了,仅供参考,互相学习。

Flitter rive加载组件 rive: ^0.9.1

rive | Flutter Package

Rive素材

Rive - Community

首先是Rive动画的基本使用

下面的代码分别演示Rive加载动画文件的三种方式。

​
import 'dart:io';import 'dart:typed_data';import 'package:flutter/material.dart';import 'package:flutter/services.dart';import 'package:path_provider/path_provider.dart';import 'package:rive/rive.dart';///author:          wangzhong///create:          2022-11-23 23:19///Description:     Rive动画的基本本使用,三种加载方式asset加载、network网络url加载、file文件加载class SimpleAnimationExample extends StatefulWidget {@overrideState<StatefulWidget> createState() {return _SimepleAnimationExampleState();}}class _SimepleAnimationExampleState extends State<SimpleAnimationExample> {String pathRive = '';@overridevoid initState() {super.initState();getFile().then((value) {setState(() {pathRive = value;});});}Future<String> getFile() async {ByteData bytes = await rootBundle.load("asset/rive/ferris-wheel.riv");String fileName = "ferris-wheel.riv";String dir = (await getApplicationSupportDirectory()).path;String filePath = "$dir/$fileName";ByteBuffer buffer = bytes.buffer;File file = await File(filePath).writeAsBytes(buffer.asUint8List());return file.path;}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('SimpleAnimationExample'),),body: Column(children: [Expanded(child: RiveAnimation.asset('asset/rive/halloween-ghost.riv',fit: BoxFit.cover,),),Expanded(child: RiveAnimation.network('https://public.rive.app/community/runtime-files/3605-7541-payfit-summit-2022.riv'),),Expanded(child: Center(child: pathRive.isEmpty? Icon(Icons.confirmation_num_outlined): RiveAnimation.file(pathRive,fit: BoxFit.cover,),),)],),);}}​

播放暂停的控制

import 'package:flutter/material.dart';import 'package:rive/rive.dart';///author:          wangzhong///create:          2022-11-24 08:50///Description:     动画播放暂停控制class PlayPauseAnimationExample extends StatefulWidget {PlayPauseAnimationExample({Key? key}) : super(key: key);@override_PlayPauseAnimationExampleState createState() =>_PlayPauseAnimationExampleState();}class _PlayPauseAnimationExampleState extends State<PlayPauseAnimationExample> {late RiveAnimationController _controller;/// Toggles between play and pause animation statesvoid _togglePlay() =>setState(() => _controller.isActive = !_controller.isActive);/// Tracks if the animation is playing by whethe r controller is runningbool get isPlaying => _controller.isActive;@overridevoid initState() {// TODO: implement initState_controller = SimpleAnimation('Timeline 1');super.initState();}@overridevoid dispose() {// TODO: implement dispose_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('PlayPauseAnimationExample'),),body: Center(child: RiveAnimation.asset('asset/rive/ferris-wheel.riv',controllers: [_controller],onInit: (state) => setState(() => print(state)),)),floatingActionButton: FloatingActionButton(onPressed: () => _togglePlay(),tooltip: isPlaying ? 'Pause' : 'Play',child: Icon(isPlaying ? Icons.pause : Icons.play_arrow,),),);}}

动画中执行单次操作

​
import 'package:flutter/material.dart';import 'package:rive/rive.dart';///author:          wangzhong///create:          2022-11-24 20:59///Description:     动画中执行单次动作class PlayOneShotExample extends StatefulWidget {PlayOneShotExample({Key? key}) : super(key: key);@override_PlayOneShotExampleState createState() => _PlayOneShotExampleState();}class _PlayOneShotExampleState extends State<PlayOneShotExample> {late RiveAnimationController _controller;/// Is the animation currently playing?bool _isPlaying = false;@overridevoid initState() {super.initState();_controller = OneShotAnimation('bounce',autoplay: false,onStop: () => setState(() => _isPlaying = false),onStart: () => setState(() => _isPlaying = true),);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('PlayOneShotExample'),// titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),),body: Center(child: RiveAnimation.network('https://cdn.rive.app/animations/vehicles.riv',animations: const ['idle', 'curves'],controllers: [_controller],),),floatingActionButton: FloatingActionButton(// disable the button while playing the animationonPressed: () => _isPlaying ? null : _controller.isActive = true,tooltip: 'Bounce',child: const Icon(Icons.arrow_upward),),);}}​

控制动画速度

import 'package:flutter/material.dart';import 'package:rive/rive.dart';import 'package:yhm_app/utils/rive_speed_controller.dart';///author:          wangzhong///create:          2022-11-25 11:40///Description:     动画速度控制class SpeedControllExample extends StatefulWidget {SpeedControllExample({Key? key}) : super(key: key);@override_SpeedControllExampleState createState() => _SpeedControllExampleState();}class _SpeedControllExampleState extends State<SpeedControllExample> {late RiveSpeedController speedController;@overridevoid initState() {// TODO: implement initStatespeedController = RiveSpeedController('Timeline 1', speedMultiplier: 1);super.initState();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('SpeedControllExample'),// titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),),body: Container(child: Column(children: [Expanded(child: RiveAnimation.asset('asset/rive/ferris-wheel.riv',fit: BoxFit.cover,// animations: const ['Timeline 1'],controllers: [speedController],)),],),),floatingActionButton: FloatingActionButton(onPressed: () {setState(() {if (speedController.speedMultiplier > 9) {speedController.speedMultiplier = 0;}speedController.speedMultiplier++;});},child: Text('x${speedController.speedMultiplier}'),),);}}// class RiveSpeedController extends SimpleAnimation {//   final double speedMultiplier;////   RiveSpeedController(//     String animationName, {//     double mix = 1,//     this.speedMultiplier = 1,//   }) : super(animationName, mix: mix);////   @override//   void apply(RuntimeArtboard artboard, double elapsedSeconds) {//     if (instance == null || !instance!.keepGoing) {//       isActive = false;//     }//     instance!//       ..animation.apply(instance!.time, coreContext: artboard, mix: mix)//       ..advance(elapsedSeconds * speedMultiplier);//   }// }

下载进度动画

import 'dart:math';import 'package:flutter/material.dart';import 'package:flutter/services.dart';import 'package:rive/rive.dart';///author:          wangzhong///create:          2022-11-26 17:40///Description:     xxxxclass DownloadProgressExample extends StatefulWidget {DownloadProgressExample({Key? key}) : super(key: key);@override_DownloadProgressExampleState createState() =>_DownloadProgressExampleState();}class _DownloadProgressExampleState extends State<DownloadProgressExample> {// SMIInput<bool> start;// SMIInput<double> progeress;Artboard? _riveArtboard;// SMIInput<bool>? _start;SMITrigger? _start;SMIInput<double>? _progress;@overridevoid initState() {rootBundle.load('asset/rive/liquid_download.riv').then((data) async {// Load the RiveFile from the binary data.final file = RiveFile.import(data);// The artboard is the root of the animation and gets drawn in the// Rive widget.final artboard = file.mainArtboard;var controller =StateMachineController.fromArtboard(artboard, 'Download');if (controller != null) {artboard.addController(controller);_start = controller.findSMI('Download');_progress = controller.findInput('Progress');print(_start);print(_progress);}setState(() => _riveArtboard = artboard);},);super.initState();}@overrideWidget build(BuildContext context) {final ThemeData theme = Theme.of(context);return Scaffold(appBar: AppBar(title: Text('DownloadProgressExample'),// titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),),body: Container(child: Column(children: [Expanded(child: _riveArtboard == null? SizedBox(): GestureDetector(onTap: () {_start?.fire();// _start?.value = true;},child: Rive(artboard: _riveArtboard!))),_riveArtboard == null? SizedBox(): SliderTheme(data: theme.sliderTheme.copyWith(activeTrackColor: Colors.deepPurple,inactiveTrackColor: Colors.blue.withAlpha(55),activeTickMarkColor:theme.colorScheme.onSurface.withOpacity(0.7),inactiveTickMarkColor:theme.colorScheme.surface.withOpacity(0.7),overlayColor:theme.colorScheme.onSurface.withOpacity(0.12),thumbColor: Colors.deepPurple,valueIndicatorColor: Colors.deepPurpleAccent,thumbShape: _CustomThumbShape(),valueIndicatorShape: _CustomValueIndicatorShape(),valueIndicatorTextStyle: theme.accentTextTheme.bodyText2!.copyWith(color: theme.colorScheme.onSurface),),child: Slider(value: _progress!.value,min: 0,max: 100,divisions: 10,//加上这个属性才会显示labelactiveColor: Colors.orangeAccent,inactiveColor: Colors.green.withAlpha(99),thumbColor: Colors.deepPurpleAccent,label: _progress!.value.toStringAsFixed(2),onChanged: (value) {setState(() {_progress?.value = value;});})),SizedBox(height: 30,)],),),);}}class _CustomThumbShape extends SliderComponentShape {static const double _thumbSize = 4.0;static const double _disabledThumbSize = 3.0;@overrideSize getPreferredSize(bool isEnabled, bool isDiscrete) {return isEnabled? const Size.fromRadius(_thumbSize): const Size.fromRadius(_disabledThumbSize);}static final Animatable<double> sizeTween = Tween<double>(begin: _disabledThumbSize,end: _thumbSize,);@overridevoid paint(PaintingContext context, Offset center,{required Animation<double> activationAnimation,required Animation<double> enableAnimation,required bool isDiscrete,required TextPainter labelPainter,required RenderBox parentBox,required SliderThemeData sliderTheme,required TextDirection textDirection,required double value,required double textScaleFactor,required Size sizeWithOverflow}) {final Canvas canvas = context.canvas;final ColorTween colorTween = ColorTween(begin: sliderTheme.disabledThumbColor,end: sliderTheme.thumbColor,);final double size = _thumbSize * sizeTween.evaluate(enableAnimation);final Path thumbPath = _downTriangle(size, center);canvas.drawPath(thumbPath,Paint()..color = colorTween.evaluate(enableAnimation) ?? Colors.blue);}}Path _upTriangle(double size, Offset thumbCenter) =>_downTriangle(size, thumbCenter, invert: true);Path _downTriangle(double size, Offset thumbCenter, {bool invert = false}) {final Path thumbPath = Path();final double height = sqrt(3.0) / 2.0;final double centerHeight = size * height / 3.0;final double halfSize = size / 2.0;final double sign = invert ? -1.0 : 1.0;thumbPath.moveTo(thumbCenter.dx - halfSize, thumbCenter.dy + sign * centerHeight);thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2.0 * sign * centerHeight);thumbPath.lineTo(thumbCenter.dx + halfSize, thumbCenter.dy + sign * centerHeight);thumbPath.close();return thumbPath;}class _CustomValueIndicatorShape extends SliderComponentShape {static const double _indicatorSize = 4.0;static const double _disabledIndicatorSize = 3.0;static const double _slideUpHeight = 30.0;@overrideSize getPreferredSize(bool isEnabled, bool isDiscrete) {return Size.fromRadius(isEnabled ? _indicatorSize : _disabledIndicatorSize);}static final Animatable<double> sizeTween = Tween<double>(begin: _disabledIndicatorSize,end: _indicatorSize,);@overridevoid paint(PaintingContext context, Offset center,{required Animation<double> activationAnimation,required Animation<double> enableAnimation,required bool isDiscrete,required TextPainter labelPainter,required RenderBox parentBox,required SliderThemeData sliderTheme,required TextDirection textDirection,required double value,required double textScaleFactor,required Size sizeWithOverflow}) {final Canvas canvas = context.canvas;final ColorTween enableColor = ColorTween(begin: sliderTheme.disabledThumbColor,end: sliderTheme.valueIndicatorColor,);final Tween<double> slideUpTween = Tween<double>(begin: 0.0,end: _slideUpHeight,);final double size = _indicatorSize * sizeTween.evaluate(enableAnimation);final Offset slideUpOffset =Offset(0.0, -slideUpTween.evaluate(activationAnimation));final Path thumbPath = _upTriangle(size, center + slideUpOffset);final Color paintColor = enableColor.evaluate(enableAnimation)?.withAlpha((255.0 * activationAnimation.value).round()) ??Colors.black;canvas.drawPath(thumbPath,Paint()..color = paintColor,);canvas.drawLine(center,center + slideUpOffset,Paint()..color = paintColor..style = PaintingStyle.stroke..strokeWidth = 2.0);labelPainter.paint(canvas,center +slideUpOffset +Offset(-labelPainter.width / 2.0, -labelPainter.height - 4.0));}}

状态器

import 'package:flutter/material.dart';import 'package:flutter/services.dart';import 'package:rive/rive.dart';///author:          wangzhong///create:          2022-11-26 17:51///Description:     xxxxclass MoreTypeStateChangeExample extends StatefulWidget {MoreTypeStateChangeExample({Key? key}) : super(key: key);@override_MoreTypeStateChangeExampleState createState() =>_MoreTypeStateChangeExampleState();}class _MoreTypeStateChangeExampleStateextends State<MoreTypeStateChangeExample> {Artboard? _riveWomenArtboard;SMIInput<double>? _levelInput;Artboard? _riveMenArtboard;SMITrigger? _crossInput;SMITrigger? _jabInput;SMITrigger? _kickInput;SMIInput<bool>? _runInput;SMIInput<double>? _turnInput;@overridevoid initState() {rootBundle.load('asset/rive/skills.riv').then((data) async {// Load the RiveFile from the binary data.final file = RiveFile.import(data);// The artboard is the root of the animation and gets drawn in the// Rive widget.final artboard = file.mainArtboard;var controller =StateMachineController.fromArtboard(artboard, 'Designer\'s Test');if (controller != null) {artboard.addController(controller);_levelInput = controller.findInput('Level');}setState(() => _riveWomenArtboard = artboard);},);rootBundle.load('asset/rive/character-controller.riv').then((data) async {// Load the RiveFile from the binary data.final file = RiveFile.import(data);// The artboard is the root of the animation and gets drawn in the// Rive widget.final artboard = file.mainArtboard;var controller =StateMachineController.fromArtboard(artboard, 'State Machine 1');if (controller != null) {artboard.addController(controller);_crossInput = controller.findSMI('crossPunch');_jabInput = controller.findSMI('jabPunch');_kickInput = controller.findSMI('sideKick');_runInput = controller.findInput('isRunning');_turnInput = controller.findInput('xDir');}setState(() => _riveMenArtboard = artboard);},);super.initState();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('MoreTypeStateChangeExample'),// titleTextStyle: TextStyle(fontSize: 20,color: Colors.black),),body: Container(child: Column(children: [Expanded(child: _riveWomenArtboard == null? SizedBox(): Rive(artboard: _riveWomenArtboard!,)),Padding(padding: const EdgeInsets.all(20),child: Row(children: [Expanded(child: ElevatedButton(onPressed: () {_levelInput?.change(0);},child: Text('baby')),),Padding(padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),child: ElevatedButton(onPressed: () {_levelInput?.change(1);},child: Text('younger')),),Expanded(child: ElevatedButton(onPressed: () {_levelInput?.change(2);},child: Text('old people')),),],),),Expanded(child: _riveMenArtboard == null? SizedBox(): Rive(artboard: _riveMenArtboard!,),),Row(children: [Expanded(child: ElevatedButton(onPressed: () {_crossInput?.fire();},child: Text('cross')),),Expanded(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 10),child: ElevatedButton(onPressed: () {_jabInput?.fire();},child: Text('jab')),),),Expanded(child: ElevatedButton(onPressed: () {_kickInput?.fire();},child: Text('kick')),),Expanded(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 10),child: ElevatedButton(onPressed: () {bool? flag = _runInput?.value;_runInput?.value = !flag!;},child: Text('run')),),),],),Padding(padding: const EdgeInsets.only(bottom: 20),child: Row(children: [Expanded(child: ElevatedButton(onPressed: () {print(_turnInput);_turnInput?.change(-1);},child: Text('turn left')),),Expanded(child: Padding(padding: const EdgeInsets.symmetric(horizontal: 10),child: ElevatedButton(onPressed: () {_turnInput?.value = 1;},child: Text('turn right')),),),],),),],),),);}}

如上图所示,状态切换,点击方块时,圆圈会做出一个亮暗的变化,模仿了一个开关灯的操作。

在rive平台编辑器上看到的在这组动画中有Inputs中有开关,Listener中有监听

Expanded(child: GestureDetector(// onTap: _hitSwitch,child: RiveAnimation.asset('asset/rive/light_switch.riv',stateMachines: ['Switch'],),),),

Rive动画使用介绍(Flutter)相关推荐

  1. Flutter介绍 - Flutter,H5,React Native之间的对比

    Flutter介绍 Flutter是Google推出的开源移动应用开发框架.开发者可以通过开发一套代码同时运行在iOS和Android平台. 它使用Dart语言进行开发,并且最终编译成各个平台的Nat ...

  2. 动画 java_Java动画程序介绍

    Java动画程序介绍 java动画的实现,首先用java.awt包中graphics类的drawimage()方法在屏幕画出图象,然后通过定义一个线程,让该线程睡眠一段时间,到时后再切换成另外一幅图象 ...

  3. php产品效果图,jQuery_基于JQuery制作的产品广告效果,效果图.如下: 动画效果介绍 - phpStudy...

    基于JQuery制作的产品广告效果 效果图.如下: 动画效果介绍:这组广告效果是打开页面后图片会自动播放,从1-5共计5张图片,如果属标放到右下角的1.2.3.4.5列表上,可以自由进行切换到自己想看 ...

  4. 属性动画、帧动画、补间动画的介绍使用及对比

    属性动画.帧动画.补间动画的介绍使用及对比 版权声明:转载必须注明本文转自南轩的博客: http://blog.csdn.net/nanxuan521 在android开发中经常会碰到一些动画需求,其 ...

  5. 动画—Keyframes介绍

    动画-Keyframes介绍 Animation动画 css3为Animation动画提供的几个属性如下: animation-name:指定动画名称,该属性指定一个已有的关键帧定义. animati ...

  6. 游戏动画专业介绍及发展

    游戏动画专业介绍及发展 对游戏动画感兴趣?想要入行?或者正打算转行游戏动画?那我今天分享的文章你一定要看.让我们一起来开启成都艺点动画这段奇妙的游戏之旅吧.本片文章围绕什么是游戏动画,游戏动画的行业发 ...

  7. 网站设计之Flash简单动画入门介绍(一)字体闪烁及渐显

    在制作网站过程中,增加些动画效果是非常美妙的一件事.由于最近在当Flash和PS课程的助教,也辅导学生完成PS.Flash.HTML等操作,所以这篇文章主要是对Flash动画的入门介绍,希望对你有所帮 ...

  8. Android之Animation动画的介绍及用法

    Android SDK介绍了2种Animation: Tween Animation(渐变动画):通过对特定的对象做图像变换如平移.缩放.旋转.淡出/淡入等产生动画效果 Frame Animation ...

  9. iOS核心动画以及UIView动画的介绍

    我们看到很多App带有绚丽狂拽的特效,别出心裁的控件设计,很大程度上提高了用户体验,在增加了实用性的同时,也赋予了app无限的生命力.这些华丽的效果很多都是基于iOS的核心动画原理实现的,本文介绍一些 ...

最新文章

  1. log4j中调试与错误日志分开_idea中log4j日志插件报错
  2. Jupyter修改默认文件保存路径
  3. BotVS开发基础—2.4 获取订单、取消订单、获取未完成订单
  4. CCF NOI1041 志愿者选拔
  5. CODEVS 2102 石子归并 2
  6. Windows更改系统字体
  7. python opencv 显示图片 灰度图片 合并图片 保存图片 纵向合并
  8. selenium利用cookie跳过验证码登录
  9. 如果你是iPhone用户,要学会这样清理手机垃圾,减缓卡顿小妙招
  10. 编译之 jack-server报错
  11. 西红柿炒鸡蛋教程(从入门到精通)
  12. 面试:如何应对人事的面试
  13. 使用帕累托最优选择解释涌现现象
  14. 命令python所在的驱动器和文件夹_Python文件夹与文件的操作-阿里云开发者社区...
  15. java 中 ajax 的学习
  16. smartctl 使用
  17. 《数学学科知识与教学能力》(高级中学)考纲
  18. XYMultipleSeriesRenderer 绘制K线图,点击弹出pop
  19. IE中报SCRIPT1004: 缺少 ‘;‘错误原因
  20. Flutter的优势以及Dart基础语法

热门文章

  1. 河南大学计算机专业一般保研到什么学校,河南大学计算机与信息工程学院(专业学位)计算机技术保研...
  2. uc/os-II 分析(9)---消息邮箱
  3. Dota2国际冠军赛如火如荼,可以邀请朋友一同观看VR直播
  4. Srvctl命令详细
  5. ffmpeg一键压特效+小丸工具箱压mod特效的方法
  6. asp.net Code学习一(vs code跨平台软件操作)
  7. windows 10下VS2019编译mongoDB c、c++API(win10下vs2019编译mongo c++)
  8. 黑马程序员--Java学习日记之集合(map集合和collections类)
  9. android在自带的app中,调用手机自带的百度地图和高德地图去实现导航的功能
  10. 线性代数让我想想:克拉默法则