Agent Skills by ALSEL
Anthropic Claudeデザイン・クリエイティブ⭐ リポ 100品質スコア 85/100

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エディタ

学習リソース

関連スキル

  • 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
リポジトリ
freshtechbro/claudedesignskills
ライセンス
MIT
最終更新
2025/11/20

Source: https://github.com/freshtechbro/claudedesignskills / ライセンス: MIT

本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: freshtechbro · freshtechbro/claudedesignskills · ライセンス: MIT