首页科学探索Flutter保姆级教程 | App如何实现复杂动画
61363

Flutter保姆级教程 | App如何实现复杂动画

我要新鲜事2023-05-19 23:39:460

作者:京东物流 沈明亮

在App开发过程中,如果想实现动画效果,可以粗略分为两种方式。一种是直接用代码编写,像平移、旋转等简单的动画效果,都可以这么干,如果稍微复杂点,就会对开发工程师的数学功底、图形图像学功底有很高的要求。

另一种方式,可以让UI同学配合,一次性出多张图片或者直接出一张GIF图,通过短时间内快速轮播图片的方式来实现复杂动画效果,这种方式真正实现起来还是有挺多问题的,比如缺少对动画过程的控制、图片尺寸的适配等等。那么,有没有更好的解决方案呢?

有的,Rive。

Rive是专门为简化动画的实现而生的,设计师可以在其官网通过拖拉拽实现各种复杂动画效果,设计完毕后导出动画文件,工程师可以在App里直接导入此文件,配合相应的SDK即可实现。

其官网有详细的开发文档,同时也有自己的社区资源,我们可以直接从社区里下载别人设计好的动画效果进行学习。另外特别重要的是,Rive支持跨平台,同时支持Android、iOS、Flutter、JS、React、C 等等,本文以Flutter的实现为例介绍。

登陆Rive官网进行设计,并导出相应的动画文件,Rive的动画文件是以.riv结尾。

本文示例是从官网的社区里找的一个个人比较喜欢的动效。

依次运行下面的命令,引入rive sdk。

将导出的.riv文件放到资源目录下,并修改pubspec.yaml文件。

加载动画文件并展示的核心代码:

核心代码就这么多,对于代码中的标注详细说明下:

标注1的地方,主要作用是获取状态机控制器,fromArtboard 方法有两个参数,第二个参数是状态机的名称,这个名称需要和UI同学协商好,一旦确定好名称就不允许设计同学再改了,对应于设计面板界面的左下角,如下图:

标注2的地方,本例的动画是根据“数值”的变化而变化的,findInput的入参同样需要和UI同学协商好,一旦设计时把这个名字改了,代码里也别忘了进行相应的修改,也在设计面板的左下角,在状态机名称的右边,如下图:

完整的代码如下,大家可以按步骤自己操作体验下。

class RiveDemo extends StatefulWidget {

const RiveDemo({Key? key}) : super(key: key);

@override

State<RiveDemo> createState() => _RiveDemoState();

}

class _RiveDemoState extends State<RiveDemo> {

/// 状态机控制器

StateMachineController? controller;

/// 控制输入数值

SMIInput<double>? valueController;

///画板,配合Rive widget 使用,展示动画效果。

Artboard? riveArtboard;

Timer? timer;

@override

void initState() {

super.initState();

//加载

rootBundle.load('asset/rives/rive_demo.riv').then((value) async {

final file = RiveFile.import(value);

final artboard = file.mainArtboard;

//1

controller = StateMachineController.fromArtboard(artboard, 'TreeMachine');

if (controller != null) {

setState(() {

artboard.addController(controller!);

//2

valueController = controller!.findInput('input');

valueController!.value = -4;

});

}

riveArtboard = artboard;

});

}

@override

void dispose() {

controller?.dispose();

stopAnimation();

super.dispose();

}

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(

title: const Text('Rive Demo'),

),

backgroundColor: Colors.white,

body: Center(

child: riveArtboard == null ? const CircularProgressIndicator() : Rive(artboard: riveArtboard!),

),

floatingActionButton: SizedBox(

height: 50,

child: Row(

mainAxisAlignment: MainAxisAlignment.end,

children: [

TextButton(

onPressed: () {

startAnimation();

},

child: const Text('start'),

),

TextButton(

onPressed: () {

stopAnimation();

},

child: const Text('stop'),

),

TextButton(

onPressed: () {

resetAnimation();

},

child: const Text('reset'),

),

],

),

),

);

}

/// 开始动画

void startAnimation() {

if (timer != null) {

return;

}

timer = Timer.periodic(const Duration(milliseconds: 60), (timer) {

valueController?.value = 0.5;

});

}

/// 停止动画

void stopAnimation() {

timer?.cancel();

timer = null;

}

/// 重置动画

void resetAnimation() {

stopAnimation();

valueController?.value = 0;

}

}

像本例中的动画效果,如果用代码来编写,时间成本会很大很大,如果靠图片的堆积,实现起来也很麻烦,而且由于图片的数量增多,安装包的体积也会增加很多。但是用rive,实现起来却很方便,可能唯一的成本就是设计师同学的学习成本。

Rive不仅支持本地动画文件的加载,还可以将动画文件放到服务器上,利用RiveAnimation.network方法进行加载。更多的使用示例可以参考:https://github.com/rive-app/rive-flutter/tree/master/example

0000
评论列表
共(0)条