教你製作強大的 Rive 動畫,完成一隻 Flutter Dash,在 APP 跟它互動!

Yii Chen
Flutter Formosa
Published in
15 min readMar 2, 2023

簡單好上手的 Rive Animation

已經想很久要製作動畫,之前想說要嘗試 Lottie 還是 Rive,動畫能製作的效果都很棒,不過 Flutter 持續有在跟 Rive 合作,社群活躍,團隊本身也有 Flutter 開發者會分享一些相關應用,讓我對它產生興趣,而且很快就能上手了,趕緊跟著我來了解吧!

  • 動畫開發服務與平台,前身為 Flare
  • Flutter 持續有在推廣 Rive 也經常合作,最新的作品為 Holobooth
  • 跨平台支援,在任何地方都能運行的交互式動畫格式,包含手機、網頁、桌面應用,當然 APP 相關有 Android、iOS、Flutter、React Native
  • 使用網頁編輯器和 MacOS 版本製作動畫
  • 免費帳戶允許創建 3 個專案,每個都可創建無限的模板,對個人來說很夠用了

官網:https://rive.app/

下載:https://rive.app/downloads

文件:https://help.rive.app/

Rive 與 Lottie 比較

  • Rive 不需要安裝額外的工具、軟體,例如:AE,可以直接在網頁上製作動畫。而現在也有了 MacOS 版本,體驗一樣好
  • Rive 擁有更多與動畫互動的功能,例如:Bone、Weight、Blend…。Lottie 為矢量 GIF,能操作的互動性有限
  • Rive 從製作到產出都是在相同平台,確保品質與效果在多平台都是一致的。Lottie 經由多個工具的輔助,他們都不是互相設計,所以沒辦法保證最後的顯示相同
  • Rive 文件更小、更快,不太消耗裝置的記憶體
  • Rive 支援團隊協作,一起設計
https://rive.app/blog/rive-as-a-lottie-alternative

官方範例

Holobooth
Holobooth

圖像製作

一進來編輯器會有提供預設的 Artboard,也就是畫板,像是程式開發的 Canvas,我們需要在上面製作、繪畫。前面有提到每個專案裡面都能新增無數個 Artboard,需要用的時候在個別導出檔案。

左邊為元素欄,我們畫的圖形、線條都會在上面展示,也可以將相關意義、區域的東西透過群組包裹,方便管理。

右邊則為自定義面板,包含基本的位置、大小、顏色,進階的有多層次顏色、裁剪、約束。

基本的矩形、圓形、多邊形和星星都可以畫,當要畫一些特別的圖象時就會用到畫筆,輕鬆能掌握弧形與對應角度,算是我最常用的一個繪畫工具

畫筆就是在特定位置點擊後產生貝茲曲線的節點,並可以進行拉伸與變換方向來改變線條,本身支援四種模式

  1. Straight → 直邊線條,預設使用方式,只需確定每個頂點就會自動連結
  2. Mirrored → 彎曲線條,在放置頂點的時候拖拉它,根據你的方向與長短產生彎曲效果,兩邊的長度拖拉時會等長
  3. Detached → 曲線,兩邊獨立的曲線節點,可以個別有自己的角度和拉伸
  4. Asymmetric → 曲線,兩邊的角度一樣,但是拉伸曲度不同
Mirrored
Detached
Asymmetric

在繪製時要注意一點,圖形的線條方向是有差異的,當兩個圖形重疊的時候,如果都是順時鐘完成那重疊部分會保留。反之如果方向不同,則重疊部分不會被填充

如果今天已經有製作了兩個圖形,他們剛好都是順時鐘完成,但是因為某些效果,又不想要重疊的地方被填充,這時候可以設置顏色裡面的規則,改為 Even-Odd,這樣就不需要考慮路徑的方向

透過線條的方式我完成了第一隻自己畫的 Dash,很有趣的過程,只是要畫出3D立體感還是有點難度,需要有一些美術技巧

動畫製作

點擊右上角的 Animate,下方會有預設的 Timeline 時間軸,可以針對每個元素、線條、圖形去設置動畫

第一個範例,我要為 Dash 的右手設置揮手效果,直覺想到的是要調整翅膀的選轉角度,所以我在一開始新增旋轉角度為0的時間戳,接著在接近 0.1 秒處設置第二個時間戳,旋轉角度為17度,最後在尾巴新增跟開始一樣的0度,要讓手回到原本的位置。這樣就能完成基本的揮手動作

第二個範例,讓 Dash 可以眨眼。眼睛的部分使用了三個元素去實作,在閉眼時要注意他們的變化時間點,像是眼白的部分就要在眼皮蓋下來時才能變化,動畫才會自然。針對三個元素我在不同時間點設置時間戳,調整 scale,並設置運作時間為 0.25 秒

預設動畫在執行的時候只跑一次,共有三種模式

  1. One Shot → 跑一次
  2. Loop → 循環,從頭開始
  3. Ping Pong → 循環,但是從頭開始,從尾巴回來

時間軸預設為 1 秒,可以自行調整運行時長和速度

State Machine 動畫控制

可以根據自定義的條件、邏輯顯示指定的動畫。

在 Animations 側邊欄,新增一個 State Machine,預設會有 EntryAny StateExit,幾種狀態,建議可以先新增一個 Idle 狀態,在載入動畫後為 Idle 狀態,等待指定動畫觸發。

此範例我有新增三個動畫,飛行、眨眼和揮手,所以我需要一些狀態變數來控制動畫,在 Inputs 裡新增了 isFlying, isWinking, isWaving 三個 Boolean 值,期望在 APP 裡能透過按鈕來開關以及觸發這些動畫。

對於 Idle 和三個狀態需要產生對應關係,並給予條件,當數值改變時動畫的狀態怎麼更新,最後還能設置離開狀態的時間。Exit Time 可以設置毫秒或是百分比,在一般的情況下會建議設為 100%,意思是等待動畫完全完成後再更改狀態,才不會有卡一半的情況,感覺會很怪

點擊播放後就能直接更新 Inputs,驗證實際互動的感覺。這裡提醒一下,Layer 面板只能跟一個動畫互動,而我這裡使用到兩層,為了在 isWaving 為 true 時,除了能夠觸發原本的揮手動畫,還希望眨眼動畫也一起動作,有更多效果

我在 Layer2 設置當 isWaving 為 true 時觸發眨眼動畫,需要在 Conditions 給予指定條件,還有返回 Idle 狀態的箭頭也記得要設定

提醒:記得不管是 Animation、State Machine 或是繪製的圖形、線條和群組,都要有易讀的命名規則,這樣在程式開發時才會有好的維護性

動畫導出

  • 導出本地檔案 → 副檔名為 .riv。本文範例只有 4kb 的大小
  • 導出雲端資源 → 展示連結、嵌入代碼,甚至是 React 的範例代碼
  • 提交到 Rive Community 跟大家分享、交流

將我的分享 Flutter Dash 分享到社群,讓有興趣的人可以瀏覽和使用

https://rive.app/community/4304-8873-flutter-dash/

Flutter Rive 開發

將導出的 .riv 檔案加入 assets 目錄,並記得在 pubspec.yaml 新增對應路徑,APP 才能夠載入

Common Usage

動畫檔裡面包括了 StateMachine 和 Animations,這邊我指定播放 Fly 動畫,讓 Dash 飛起來。這種使用方式就很適合作為 Loading indicator,再資料載入時顯示出來

const AspectRatio(
aspectRatio: 1,
child: RiveAnimation.asset(
'assets/animations/dash.riv',
fit: BoxFit.cover,
animations: ['Fly'],
),
),

State Machine

宣告 Artboard、StateMachineController,還有動畫使用的狀態控制 SMIInput,並在 initState() 進行 Rive 初始化

Artboard? _artboard;
StateMachineController? _stateMachineController;

SMIInput<bool>? _flyInput;
SMIInput<bool>? _waveInput;
SMIInput<bool>? _winkInput;

@override
void initState() {
super.initState();

_initRive();
}
  1. 首先透過 rootBundle 載入檔案,取得 RiveFile 實體,並透過它取得我們編輯的面板,如果只有一個的話就可以使用 mainArtboard 屬性
  2. 接著取得 StateMachineController,它掌管動畫的生命週期,我們需要它來與動畫互動,告訴它需要連結哪個畫布
  3. 將自定義的 SMIInput 狀態控制物件記錄下來,責控制動畫參數,更新動畫
void _initRive() {
rootBundle.load('assets/animations/dash.riv').then(
(data) async {
final file = RiveFile.import(data);

final artboard = file.mainArtboard;
final controller = StateMachineController.fromArtboard(artboard, 'State Machine 1');

if (controller != null) {
artboard.addController(controller);
_flyInput = controller.findInput('isFlying');
_waveInput = controller.findInput('isWaving');
_winkInput = controller.findInput('isWinking');
}

_stateMachineController = controller;
_artboard = artboard;
setState(() {});
},
);
}

此範例簡單透過三個按鈕來執行動畫,只需要更新為 true,就能觸發動畫。反之設為 false 就是停止並返回 Idle 狀態

ElevatedButton(
onPressed: () {
_resetToIdle();
setState(() {
_flyInput?.value = !(_flyInput?.value ?? false);
});
},
child: const Text('Fly'),
),

_resetToIdle() 負責重置狀態回 Idle,等待觸發新的動畫

void _resetToIdle() {
_flyInput?.value = false;
_waveInput?.value = false;
_winkInput?.value = false;
setState(() {});
}

Other Effects

更進階的動畫效果還有 Bone,針對人物或是東西可以設置骨頭,代表可以控制所在區塊進行旋轉,例如:人物的腳可以蹲下、人物帶的圍巾可以隨風飄逸,能夠玩出非常多花樣,只要你夠有創意和想法的話

還有 Meshes(網格),針對圖形標示多個區塊,透過原點的移動來實現變形

更多資訊可以查看官方說明

https://help.rive.app/editor/manipulating-shapes/bones

Rive Community

當然 Rive 也有自己的社群,大家都可以將自己的作品分享出去,可以互相學習,我們也能看別人是如何實作一些效果,覺得不錯的話可以直接在 APP 裡使用,不覺得很棒嗎

Conclusion

到這邊大家應該了解 Rive 不少吧,它讓我們很輕鬆的製作動畫,即便團隊缺少 UI 或是動畫設計師,我們也可以透過學習很快地上手,幫公司的 APP 做出開場動畫、載入動畫,設置是與使用者的互動效果,對於開發者來說很友善。官方也很常在 Youtube 頻道進行直播,有例行的團隊會議還有手把手教學,在於社群活躍度來說很健康,加上 Flutter 屢次的推廣與合作,想必 Rive 應該會持續火熱!

它是一個很值得你去了解的產品,未來如果有一些改版或是想法,我都會在 Twitter 和 Medium 分享,也歡迎留言跟我交流討論!

Reference

About

Contribution

如果覺得文章不錯的話可以贊助,讓我有更多動力和熱情分享學習紀錄和生活!請我喝一杯咖啡吧~

https://www.buymeacoffee.com/yiichenhi

希望有幫助到你/妳,歡迎追蹤我,方便瀏覽最新的文章~

--

--

Yii Chen
Flutter Formosa

Flutter Lover || Organizer FlutterTaipei || Writer, Speaker || wanna make Flutter strong in Taiwan. https://linktr.ee/yiichenhi