rive-interactive
ステートマシンベースのベクターアニメーションで、ランタイムインタラクティビティとウェブ統合を実現します。インタラクティブなアニメーション、ステート駆動のUI、ロジック付きアニメーション化されたコンポーネント、またはランタイム制御が可能なデザイナー作成アニメーションの構築時に活用できます。Riveやステートマシン、インタラクティブなベクターアニメーション、入力処理を伴うアニメーション、ViewModelのデータバインディング、またはReact Rive統合に関わるタスクでトリガーします。ステートマシンと双方向インタラクティビティが必要なアニメーションについて、Lottieの代替手段として機能します。
description の原文を見る
State machine-based vector animation with runtime interactivity and web integration. Use this skill when creating interactive animations, state-driven UI, animated components with logic, or designer-created animations with runtime control. Triggers on tasks involving Rive, state machines, interactive vector animations, animation with input handling, ViewModel data binding, or React Rive integration. Alternative to Lottie for animations requiring state machines and two-way interactivity.
SKILL.md 本文
Rive Interactive - ステートマシンベースのベクターアニメーション
概要
Riveはステートマシンベースのアニメーションプラットフォームで、デザイナーが複雑なロジックとランタイムインタラクティビティを備えたインタラクティブなベクターアニメーションを作成できます。タイムラインのみのアニメーションツール(Lottieなど)とは異なり、Riveはステートマシン、入力処理、アプリケーションコードとアニメーション間の双方向データバインディングをサポートしています。
主な機能:
- 複雑なインタラクティブロジック用のステートマシンシステム
- 双方向データバインディング用のViewModelAPI
- 入力処理(ブール値、数値、トリガー入力)
- アニメーション-コード通信用のカスタムイベント
- ランタイムプロパティ制御(色、文字列、数値、列挙型)
- クロスプラットフォーム対応(Web、React、React Native、iOS、Android、Flutter)
- ベクターグラフィックスによる小さいファイルサイズ
このスキルを使用する場合:
- 複雑な状態遷移を伴うUIアニメーションの作成
- インタラクティブなアニメーションコンポーネント(ボタン、トグル、ローダー)の構築
- ステートドリブンアニメーションを備えたゲーム的UIの実装
- リアルタイムデータをアニメーション化されたビジュアライゼーションにバインド
- ユーザー入力に応答するアニメーションの作成
- ランタイム制御を必要とするデザイナー作成アニメーションの操作
代替案:
- Lottie(lottie-animations): ステートマシンなしのシンプルなタイムラインベースアニメーション用
- Framer Motion(motion-framer): スプリングフィジックスを備えたコードファーストのReactアニメーション用
- GSAP(gsap-scrolltrigger): 正確な制御を備えたタイムラインベースのWebアニメーション用
コアコンセプト
1. ステートマシン
ステートマシンは状態と遷移によってアニメーション動作を定義します:
- 状態: 異なるアニメーション状態(例: アイドル、ホバー、押下)
- 入力: 遷移を制御する変数(ブール値、数値、トリガー)
- 遷移: 状態間の移動ルール
- リスナー: 状態変化に応答するReactフック
2. 入力
3種類の入力がステートマシン動作を制御します:
- ブール値: オン/オフ状態(例: isHovered、isActive)
- 数値: 数値(例: progress、volume)
- トリガー: ワンタイムイベント(例: click、submit)
3. ViewModels
動的プロパティ用のデータバインディングシステム:
- 文字列プロパティ: テキストコンテンツ(例: username、title)
- 数値プロパティ: 数値データ(例: stock price、score)
- 色プロパティ: 動的色(16進数値)
- 列挙型プロパティ: 定義済みオプションから選択
- トリガープロパティ: アニメーションイベント
4. イベント
アニメーションから発行されるカスタムイベント:
- 一般イベント: カスタム名前付きイベント
- イベントプロパティ: イベントに付属するデータ
- イベントリスナー: イベントを処理するReactフック
一般的なパターン
パターン1: 基本的なRiveアニメーション
ユースケース: Reactで簡単なRiveアニメーションを表示
実装:
# インストール
npm install rive-react
import Rive from 'rive-react';
export default function SimpleAnimation() {
return (
<Rive
src="animation.riv"
artboard="Main"
animations="idle"
layout={{ fit: "contain", alignment: "center" }}
style={{ width: '400px', height: '400px' }}
/>
);
}
重要なポイント:
src: .rivファイルへのパスartboard: 表示するアートボードanimations: 再生するアニメーションタイムラインlayout: コンテナ内でアニメーションを配置する方法
パターン2: 入力を使用したステートマシン制御
ユースケース: ユーザーインタラクションに基づいてアニメーション状態を制御
実装:
import { useRive, useStateMachineInput } from 'rive-react';
export default function InteractiveButton() {
const { rive, RiveComponent } = useRive({
src: 'button.riv',
stateMachines: 'Button State Machine',
autoplay: true,
});
// ステートマシン入力を取得
const hoverInput = useStateMachineInput(
rive,
'Button State Machine',
'isHovered',
false
);
const clickInput = useStateMachineInput(
rive,
'Button State Machine',
'isClicked',
false
);
return (
<div
onMouseEnter={() => hoverInput && (hoverInput.value = true)}
onMouseLeave={() => hoverInput && (hoverInput.value = false)}
onClick={() => clickInput && clickInput.fire()} // トリガー入力
style={{ cursor: 'pointer' }}
>
<RiveComponent style={{ width: '200px', height: '100px' }} />
</div>
);
}
入力タイプ:
- ブール値:
input.value = true/false - 数値:
input.value = 50 - トリガー:
input.fire()
パターン3: ViewModelデータバインディング
ユースケース: アプリケーションデータをアニメーションプロパティにバインド
実装:
import { useRive, useViewModel, useViewModelInstance,
useViewModelInstanceString, useViewModelInstanceNumber } from 'rive-react';
import { useEffect, useState } from 'react';
export default function Dashboard() {
const [stockPrice, setStockPrice] = useState(150.0);
const { rive, RiveComponent } = useRive({
src: 'dashboard.riv',
autoplay: true,
autoBind: false, // ViewModel用に手動バインディング
});
// ViewModelとインスタンスを取得
const viewModel = useViewModel(rive, { name: 'Dashboard' });
const viewModelInstance = useViewModelInstance(viewModel, { rive });
// プロパティをバインド
const { setValue: setTitle } = useViewModelInstanceString(
'title',
viewModelInstance
);
const { setValue: setPrice } = useViewModelInstanceNumber(
'stockPrice',
viewModelInstance
);
useEffect(() => {
if (setTitle) setTitle('Stock Dashboard');
}, [setTitle]);
useEffect(() => {
if (setPrice) setPrice(stockPrice);
}, [setPrice, stockPrice]);
// リアルタイムアップデートをシミュレート
useEffect(() => {
const interval = setInterval(() => {
setStockPrice((prev) => prev + (Math.random() - 0.5) * 10);
}, 1000);
return () => clearInterval(interval);
}, []);
return <RiveComponent style={{ width: '800px', height: '600px' }} />;
}
ViewModelプロパティフック:
useViewModelInstanceString- テキストプロパティuseViewModelInstanceNumber- 数値プロパティuseViewModelInstanceColor- 色プロパティ(16進数)useViewModelInstanceEnum- 列挙型選択useViewModelInstanceTrigger- アニメーショントリガー
パターン4: Riveイベントの処理
ユースケース: Riveアニメーションから発行されたイベントに反応
実装:
import { useRive, EventType, RiveEventType } from 'rive-react';
import { useEffect } from 'react';
export default function InteractiveRating() {
const { rive, RiveComponent } = useRive({
src: 'rating.riv',
stateMachines: 'State Machine 1',
autoplay: true,
automaticallyHandleEvents: true,
});
useEffect(() => {
if (!rive) return;
const onRiveEvent = (event) => {
const eventData = event.data;
if (eventData.type === RiveEventType.General) {
console.log('Event:', eventData.name);
// イベントプロパティにアクセス
const rating = eventData.properties.rating;
const message = eventData.properties.message;
if (rating >= 4) {
alert(`Thanks for ${rating} stars: ${message}`);
}
}
};
rive.on(EventType.RiveEvent, onRiveEvent);
return () => {
rive.off(EventType.RiveEvent, onRiveEvent);
};
}, [rive]);
return <RiveComponent style={{ width: '400px', height: '300px' }} />;
}
パターン5: Riveファイルの事前読み込み
ユースケース: 読み込み時間を最適化するためにアニメーションを事前読み込み
実装:
import { useRiveFile, useRive } from 'rive-react';
export default function PreloadedAnimation() {
const { riveFile, status } = useRiveFile({
src: 'large-animation.riv',
});
const { RiveComponent } = useRive({
riveFile: riveFile,
artboard: 'Main',
autoplay: true,
});
if (status === 'loading') {
return <div>Loading animation...</div>;
}
if (status === 'failed') {
return <div>Failed to load animation</div>;
}
return <RiveComponent style={{ width: '600px', height: '400px' }} />;
}
パターン6: 参照を使用した制御アニメーション
ユースケース: 親コンポーネントからアニメーションを制御
実装:
import { useRive, useViewModel, useViewModelInstance,
useViewModelInstanceTrigger } from 'rive-react';
import { useImperativeHandle, forwardRef } from 'react';
const AnimatedComponent = forwardRef((props, ref) => {
const { rive, RiveComponent } = useRive({
src: 'logo.riv',
autoplay: true,
autoBind: false,
});
const viewModel = useViewModel(rive, { useDefault: true });
const viewModelInstance = useViewModelInstance(viewModel, { rive });
const { trigger: spinTrigger } = useViewModelInstanceTrigger(
'triggerSpin',
viewModelInstance
);
// メソッドを親に公開
useImperativeHandle(ref, () => ({
spin: () => spinTrigger && spinTrigger(),
pause: () => rive && rive.pause(),
play: () => rive && rive.play(),
}));
return <RiveComponent style={{ width: '200px', height: '200px' }} />;
});
export default function App() {
const animationRef = useRef();
return (
<div>
<AnimatedComponent ref={animationRef} />
<button onClick={() => animationRef.current?.spin()}>Spin</button>
<button onClick={() => animationRef.current?.pause()}>Pause</button>
</div>
);
}
パターン7: 複数プロパティのViewModelアップデート
ユースケース: 複雑なデータから複数のアニメーションプロパティをアップデート
実装:
import { useRive, useViewModel, useViewModelInstance,
useViewModelInstanceString, useViewModelInstanceNumber,
useViewModelInstanceColor } from 'rive-react';
import { useEffect } from 'react';
export default function UserProfile({ user }) {
const { rive, RiveComponent } = useRive({
src: 'profile.riv',
autoplay: true,
autoBind: false,
});
const viewModel = useViewModel(rive, { useDefault: true });
const viewModelInstance = useViewModelInstance(viewModel, { rive });
// すべてのプロパティをバインド
const { setValue: setName } = useViewModelInstanceString('name', viewModelInstance);
const { setValue: setScore } = useViewModelInstanceNumber('score', viewModelInstance);
const { setValue: setColor } = useViewModelInstanceColor('avatarColor', viewModelInstance);
useEffect(() => {
if (user && setName && setScore && setColor) {
setName(user.name);
setScore(user.score);
setColor(parseInt(user.color.substring(1), 16)); // 16進数を数値に変換
}
}, [user, setName, setScore, setColor]);
return <RiveComponent style={{ width: '300px', height: '300px' }} />;
}
インテグレーションパターン
Framer Motionとの連携(motion-framer)
Riveでインタラクティブコンテンツをハンドルしながらコンテナをアニメーション:
import { motion } from 'framer-motion';
import Rive from 'rive-react';
export default function AnimatedCard() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
whileHover={{ scale: 1.05 }}
>
<Rive
src="card.riv"
stateMachines="Card State Machine"
style={{ width: '300px', height: '400px' }}
/>
</motion.div>
);
}
GSAP ScrollTriggerとの連携(gsap-scrolltrigger)
スクロール時にRiveアニメーションをトリガー:
import { useRive, useStateMachineInput } from 'rive-react';
import { useEffect, useRef } from 'react';
import gsap from 'gsap';
import ScrollTrigger from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
export default function ScrollRive() {
const containerRef = useRef();
const { rive, RiveComponent } = useRive({
src: 'scroll-animation.riv',
stateMachines: 'State Machine 1',
autoplay: true,
});
const trigger = useStateMachineInput(rive, 'State Machine 1', 'trigger');
useEffect(() => {
if (!trigger) return;
ScrollTrigger.create({
trigger: containerRef.current,
start: 'top center',
onEnter: () => trigger.fire(),
});
}, [trigger]);
return (
<div ref={containerRef}>
<RiveComponent style={{ width: '100%', height: '600px' }} />
</div>
);
}
パフォーマンス最適化
1. オフスクリーンレンダラーの使用
<Rive
src="animation.riv"
useOffscreenRenderer={true} // より良いパフォーマンス
/>
2. Riveファイルの最適化
Riveエディタで:
- アートボードを2MB以下に抑える
- ベクターグラフィックスを使用(可能な限りラスター画像を避ける)
- スケレタルアニメーションのボーン数を最小化
- ステートマシンの複雑さを削減
3. 重要なアニメーションの事前読み込み
const { riveFile } = useRiveFile({ src: 'critical.riv' });
// アプリケーション初期化中に事前読み込み
4. 自動イベント処理を無効化
<Rive
src="animation.riv"
automaticallyHandleEvents={false} // 手動制御
/>
一般的な落とし穴と解決策
落とし穴1: ステートマシン入力が見つからない
問題: useStateMachineInputがnullを返す
解決策:
// ❌ 間違い: 入力名が正しくない
const input = useStateMachineInput(rive, 'State Machine', 'wrongName');
// ✅ 正しい: Riveエディタから正確な名前と一致
const input = useStateMachineInput(rive, 'State Machine', 'isHovered');
// 使用前に常に入力の存在を確認
if (input) {
input.value = true;
}
落とし穴2: ViewModelプロパティが更新されない
問題: ViewModelプロパティがアニメーションを更新しない
解決策:
// ❌ 間違い: autoBindが有効化されている
const { rive } = useRive({
src: 'dashboard.riv',
autoplay: true,
// autoBind: true (デフォルト)
});
// ✅ 正しい: ViewModelの手動制御のためにautoBindを無効化
const { rive } = useRive({
src: 'dashboard.riv',
autoplay: true,
autoBind: false, // ViewModelの手動制御に必須
});
落とし穴3: イベントリスナーが発火しない
問題: Riveイベントがコールバックをトリガーしない
解決策:
// ❌ 間違い: automaticallyHandleEventsが不足
const { rive } = useRive({
src: 'rating.riv',
stateMachines: 'State Machine 1',
autoplay: true,
});
// ✅ 正しい: イベント処理を有効化
const { rive } = useRive({
src: 'rating.riv',
stateMachines: 'State Machine 1',
autoplay: true,
automaticallyHandleEvents: true, // イベント処理に必須
});
リソース
公式ドキュメント
- Rive Docs: https://rive.app/docs
- React Rive GitHub: https://github.com/rive-app/rive-react
- Riveコミュニティ: https://rive.app/community
Riveエディタ
- Webエディタ: https://rive.app/community
- デスクトップアプリ: macOS、Windows向けに提供
学習リソース
- チュートリアル: https://rive.app/learn
- 例: https://rive.app/community/files
- ステートマシンガイド: https://rive.app/docs/state-machine
関連スキル
- lottie-animations: ステートマシンなしのシンプルなタイムラインベースアニメーション用
- motion-framer: ジェスチャー付きコードファーストのReactアニメーション用
- gsap-scrolltrigger: スクロール駆動アニメーション用
- spline-interactive: 3Dインタラクティブアニメーション用
スクリプト
このスキルには以下のユーティリティスクリプトが含まれています:
component_generator.py- Rieve Reactコンポーネントボイラープレートを生成viewmodel_builder.py- ViewModelプロパティバインディングを構築
スキルディレクトリからスクリプトを実行:
./scripts/component_generator.py
./scripts/viewmodel_builder.py
アセット
スターターテンプレートと例:
starter_rive/- 完全なReact + Riveテンプレートexamples/- 実践的なインテグレーションパターン
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- freshtechbro
- ライセンス
- MIT
- 最終更新
- 2025/11/20
Source: https://github.com/freshtechbro/claudedesignskills / ライセンス: MIT