mcpField
R
reanimated-skia-performance

reanimated-skia-performance

github.com/andreev-danilaデザイン・UI24956 回インストール14 週間前に更新
リポジトリ

reanimated-skia-performance について

reanimated-skia-performanceは、ReanimatedとSkiaを使ったアニメーションの実装と調整を支援するスキルです。UIスレッド上の状態管理、レンダラーやアニメーション手法の選択、性能上の注意点を開発者に案内します。

主な機能

  1. 01アニメーション状態をUIスレッドに保持
  2. 02ReanimatedとSkiaから選択
  3. 03宣言的なトランジションとアニメーションプリミティブ
  4. 04共有値をSkiaへ直接渡す
  5. 05フレームごとの描画と割り当てを回避
  6. 06性能資料とチェックリストを参照

活用シーン

  1. 01アニメーション効果とジェスチャー動作の設計
  2. 02変形やカスタム描画に使うレンダラーの選択
  3. 03Skiaシェーダーのユニフォームをアニメーション
  4. 04アニメーションコードの性能問題を確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/andreev-danila/skills --skill reanimated-skia-performance

インストール後、Claude Code で/reanimated-skia-performanceを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
dev-tuning.md
perf-checklist.md
reanimated-v4.md
skia-shaders.md

詳細

Reanimated + Skia のパフォーマンス

デフォルト設定

  • アニメーションの状態は UI スレッド上に保ちます:useSharedValue、useDerivedValue、ワークレットを使用します。
  • Reanimated v4 の宣言的 API を優先し、レガシーな Animated は避けます。
  • アプリコードでは shared.value よりも shared.get() / shared.set() を優先します(React Compiler に適しています)。
  • JS↔UI 間の呼び出しを最小限に抑えます。避けられない副作用を除き、scheduleOnRN / runOnJS は避けてください。
  • Skia では、フレームごとの React レンダリングを避けます。SharedValue を Skia の props / uniform に直接渡します。

ワークフロー

  1. エフェクトを定義します:アニメーションする要素、時間/カーブ、中断ルール、ジェスチャー入力を決めます。
  2. レンダラーを選びます:
    • 変換/不透明度/レイアウトには Reanimated のスタイルを使用します。
    • カスタム描画、パーティクル、グラデーション、ランタイムエフェクト/シェーダーには Skia を使用します。
  3. プリミティブを選びます:
    • シンプルな宣言的スタイル変更には Reanimated の CSS トランジション/アニメーションを使用します。
    • トゥイーンには withTiming、物理ベースの動きには withSpring、慣性には withDecay を使用します。
    • マウント/アンマウントやレイアウトの変更には Layout Animations を使用します。
  4. UI スレッド上で単一のデータフローを実装します(フレームごとの setState は使用しません)。
  5. パフォーマンスを確認します(references/perf-checklist.md を参照)。

パターン

Shared values(React Compiler 対応)
  • 読み取り:progress.get()
  • 書き込み:progress.set(withTiming(1))
import { useEffect } from 'react';
import { useSharedValue, withTiming } from 'react-native-reanimated';

const progress = useSharedValue(0);

useEffect(() => {
  progress.set(withTiming(1, { duration: 400 }));
}, [progress]);
Reanimated v4 の CSS トランジション

カスタムワークレットが不要な、状態駆動のスタイル変更に使用します。

import Animated from 'react-native-reanimated';

<Animated.View
  style={{
    width: expanded ? 240 : 160,
    opacity: enabled ? 1 : 0.6,
    transitionProperty: ['width', 'opacity'],
    transitionDuration: 220,
    transitionTimingFunction: 'ease-in-out',
  }}
/>
Reanimated v4 の CSS アニメーション(キーフレーム)

カスタムワークレットを書かずに、キーフレーム風のシーケンス(パルス、揺れ、繰り返しループ)を実現する場合に使用します。

対応する設定:

  • animationName(キーフレームオブジェクト)
  • animationDuration
  • animationDelay
  • animationTimingFunction
  • animationDirection
  • animationIterationCount
  • animationFillMode
  • animationPlayState
import Animated from 'react-native-reanimated';

const pulse = {
  from: { transform: [{ scale: 1 }], opacity: 0.9 },
  '50%': { transform: [{ scale: 1.06 }], opacity: 1 },
  to: { transform: [{ scale: 1 }], opacity: 0.9 },
};

<Animated.View
  style={{
    animationName: pulse,
    animationDuration: '900ms',
    animationDelay: '80ms',
    animationTimingFunction: 'ease-in-out',
    animationDirection: 'alternate',
    animationIterationCount: 'infinite',
    animationFillMode: 'both',
    animationPlayState: paused ? 'paused' : 'running',
  }}
/>
開発モードの調整パネル(スライダー)

__DEV__ でアニメーション設定やシェーダーの uniform を調整するには、スライダーを使用します。

  • アニメーションシーンの再レンダリングを避けるため、調整 UI は別のコンポーネントに配置します。
  • .set() を使って、スライダーの値を SharedValue に書き込みます。
  • シェーダーの調整では、useDerivedValue を使って SharedValue を uniform に渡します。
  • アニメーション設定の調整では、設定パラメーターを SharedValue に保存し、アニメーションの開始/再開時に読み取ります。

参照:references/dev-tuning.md。

ジェスチャー駆動のアニメーション(Gesture Builder API)

onUpdate で共有値を更新し、アニメーションスタイルを使って表示を制御します。

import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';

const x = useSharedValue(0);
const gesture = Gesture.Pan()
  .onUpdate((e) => {
    x.set(e.translationX);
  })
  .onEnd(() => {
    x.set(withSpring(0));
  });

const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.get() }] }));

<GestureDetector gesture={gesture}>
  <Animated.View style={style} />
</GestureDetector>;
アニメーションする uniform を使った Skia シェーダー
  • ランタイムエフェクトは一度だけコンパイルします(useMemo)。
  • uniform は SharedValue<Uniforms> として渡します(Skia は { value: T } によってアニメーションする props を検出します)。
  • アプリコードで .value にアクセスせず、SharedValue オブジェクトを渡します。
import { useMemo } from 'react';
import { Skia, Canvas, Fill, Paint, Shader, type Uniforms } from '@shopify/react-native-skia';
import { useDerivedValue } from 'react-native-reanimated';
import { useClock } from '@shopify/react-native-skia';

const sksl = `
uniform float2 u_resolution;
uniform float u_time;

half4 main(float2 xy) {
  float2 uv = xy / u_resolution;
  float v = 0.5 + 0.5 * sin(u_time * 0.002 + uv.x * 8.0);
  return half4(v, v * 0.6, 1.0 - v, 1.0);
}
`;

const effect = useMemo(() => Skia.RuntimeEffect.Make(sksl), []);
if (!effect) return null;

const clock = useClock(); // ms since first frame
const uniforms = useDerivedValue<Uniforms>(() => ({
  u_resolution: Skia.Point(width, height),
  u_time: clock.get(),
}));

<Canvas style={{ width, height }}>
  <Fill>
    <Paint>
      <Shader source={effect} uniforms={uniforms} />
    </Paint>
  </Fill>
</Canvas>;

よくある問題

  • React のレンダー中に共有値を読み取らないでください。ワークレット(useAnimatedStyle、useDerivedValue)内で読み取ります。
  • 必要な場合を除き、onUpdate 内で runOnJS / scheduleOnRN を呼び出さないでください。
  • フレームごとに大きな配列/パス/画像を確保しないでください。Skia オブジェクトをメモ化し、アニメーションする props を通じて更新します。
  • ランタイムエフェクトでは、シェーダーで宣言したすべての uniform を必ず指定してください。不足している uniform があると例外が発生します。

参考資料

  • Reanimated v4 のパターンとリポジトリ規約:references/reanimated-v4.md
  • Skia Canvas とランタイムエフェクト/シェーダーのパターン:references/skia-shaders.md
  • 開発モードの調整パネル(スライダー):references/dev-tuning.md
  • パフォーマンスチェックリストとデバッグ:references/perf-checklist.md

コメント