新一代跨平台、高效能動畫庫 - 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>