新一代跨平台、高效能動畫庫 - Rive
- August 31, 2023
本文將介紹一款非常令人驚豔的動畫庫及格式,具有非常高效能及跨平台的優勢,且具有 狀態
及 可互動性
,相關生態系已逐漸完整,常受到一些動畫庫效能困擾的人絕對可以試試!
Rive 簡介
Rive 是從 flutter 社群開始發跡,主要是透過 Rive 的編輯器產出開源格式 .riv
檔進行傳輸,在 Web 前端上使用 JS/WASM runtime 進行渲染,主要是 canvas + wasm,效能非常好且 GPU memory 的使用量遠比其他動畫庫低出許多。
值得注意的是雖然檔案格式跟 Runtime 都是開源的,不過編輯器是有收費制度的,免費的編輯器會有一些限制,可先使用免費再決定是否要進階付費,跟 Zeplin 的付費模式蠻像的。
實際應用
目前 Figma 的官網有大量使用 Rive,效果非常出色。
效能
效能在動畫庫中算是很不錯的,以前使用過 dragonbones,但後續已無人繼續維護且記憶體控管上稍微不理想,官方甚至有跟 Lottie 對比,詳情可以參考此篇文章
範例
以下範例將以 Vue 進行示範
Install
pnpm add @rive-app/canvas
基礎用法
基礎用法非常簡單,為方便展示寫在同一個 component,可自行進行後續封裝。
<template>
<div class="flex justify-center">
<canvas
ref="canvas"
width="400"
height="400"
class="w-full max-w-[400px]"
></canvas>
</div>
</template>
<script setup lang="ts">
import { Rive } from "@rive-app/canvas";
import { onMounted, ref } from "vue";
const canvas = ref<HTMLCanvasElement | null>(null);
onMounted(() => {
if (!canvas.value) {
throw new Error("canvas not found");
}
const rive = new Rive({
canvas: canvas.value,
src: "https://cdn.rive.app/animations/vehicles.riv",
autoplay: true,
onLoad: () => {
rive.resizeDrawingSurfaceToCanvas();
},
});
});
</script>
另人驚豔的狀態切換
試著按下以下按鈕並觀察變化吧!
<template>
<div class="flex flex-col items-center justify-center">
<canvas
ref="canvas"
width="400"
height="400"
class="w-full max-w-[400px]"
></canvas>
<div class="flex gap-2 p-4">
<button
v-for="(button, key) in buttons"
:key="key"
class="p-2 bg-gray-300 rounded-md"
@click="button.onClick"
>
{{ button.text }}
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { Rive, Layout } from "@rive-app/canvas";
import { onMounted, ref } from "vue";
const canvas = ref<HTMLCanvasElement | null>(null);
let truck: Rive;
const buttons = ref({
idle: {
text: "開車",
onClick: () => {
if (!truck) {
throw new Error("truck not found.");
}
const isPlaying = truck.playingAnimationNames.includes("idle");
buttons.value.idle.text = isPlaying ? "開車" : "停車";
isPlaying ? truck.pause("idle") : truck.play("idle");
},
},
windshield_wipers: {
text: "開雨刷",
onClick: () => {
if (!truck) {
throw new Error("truck not found.");
}
const isPlaying =
truck.playingAnimationNames.includes("windshield_wipers");
buttons.value.windshield_wipers.text = isPlaying ? "開雨刷" : "關雨刷";
isPlaying
? truck.pause("windshield_wipers")
: truck.play("windshield_wipers");
},
},
});
onMounted(() => {
if (!canvas.value) {
throw new Error("canvas not found");
}
truck = new Rive({
canvas: canvas.value,
src: "https://cdn.rive.app/animations/vehicles.riv",
artboard: "Jeep",
onLoad: () => {
truck.resizeDrawingSurfaceToCanvas();
},
});
});
</script>