reanimated-skia-performance について
reanimated-skia-performanceは、ReanimatedとSkiaを使ったアニメーションの実装と調整を支援するスキルです。UIスレッド上の状態管理、レンダラーやアニメーション手法の選択、性能上の注意点を開発者に案内します。
主な機能
- 01アニメーション状態をUIスレッドに保持
- 02ReanimatedとSkiaから選択
- 03宣言的なトランジションとアニメーションプリミティブ
- 04共有値をSkiaへ直接渡す
- 05フレームごとの描画と割り当てを回避
- 06性能資料とチェックリストを参照
活用シーン
- 01アニメーション効果とジェスチャー動作の設計
- 02変形やカスタム描画に使うレンダラーの選択
- 03Skiaシェーダーのユニフォームをアニメーション
- 04アニメーションコードの性能問題を確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ andreev-danila/ skills --skill reanimated-skia-performanceインストール後、Claude Code で/reanimated-skia-performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/dev-tuning.mdperf-checklist.mdreanimated-v4.mdskia-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 に直接渡します。
ワークフロー
- エフェクトを定義します:アニメーションする要素、時間/カーブ、中断ルール、ジェスチャー入力を決めます。
- レンダラーを選びます:
- 変換/不透明度/レイアウトには Reanimated のスタイルを使用します。
- カスタム描画、パーティクル、グラデーション、ランタイムエフェクト/シェーダーには Skia を使用します。
- プリミティブを選びます:
- シンプルな宣言的スタイル変更には Reanimated の CSS トランジション/アニメーションを使用します。
- トゥイーンには
withTiming、物理ベースの動きにはwithSpring、慣性にはwithDecayを使用します。 - マウント/アンマウントやレイアウトの変更には Layout Animations を使用します。
- UI スレッド上で単一のデータフローを実装します(フレームごとの
setStateは使用しません)。 - パフォーマンスを確認します(
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(キーフレームオブジェクト)animationDurationanimationDelayanimationTimingFunctionanimationDirectionanimationIterationCountanimationFillModeanimationPlayState
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
コメント
Markdown 対応