在App開發過程中,如果想實現動畫效果,可以粗略分為兩種方式。一種是直接用代碼編寫,像平移、旋轉等簡單的動畫效果,都可以這麼乾,如果稍微複雜點,就會對開發工程師的數學功底、圖形圖像學功底有很高的要求。 ...
作者:京東物流 沈明亮
在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