教你製作強大的 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