mcpField
R
react-native-ease-refactor

react-native-ease-refactor

github.com/appandflow開発992946 回インストール2 週間前に更新
リポジトリ

react-native-ease-refactor について

react-native-ease refactor は、対応する React Native Reanimated と Animated のコードを react-native-ease の EaseView コンポーネントへ移行するスキルです。アニメーションを調査して移行可否を報告し、変更前に確認を求めます。

主な機能

  1. 01Reanimated と Animated の使用箇所を調査
  2. 02移行可能なアニメーションパターンを分類
  3. 03タイミング・スプリング・イージング設定を変換
  4. 04非対応パターンと理由を報告
  5. 05編集前に対象コンポーネントを選択

活用シーン

  1. 01状態駆動アニメーションを EaseView に移行
  2. 02移行範囲の選択前にアニメーションコードを確認
  3. 03移行できないジェスチャーやスクロール駆動を特定

インストール

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

$npx skills add https://github.com/appandflow/react-native-ease --skill react-native-ease-refactor

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

詳細

react-native-ease のリファクタリング

react-native-reanimated および React Native 組み込みの Animated API のコードを、react-native-ease の EaseView コンポーネントに変換する移行アシスタントです。

以下の6つのフェーズを、必ず指定の順序どおりに実行してください。フェーズを省略したり、順序を入れ替えたりしないでください。


Phase 1: 検出

ユーザーのプロジェクトをスキャンして、アニメーションコードを検出します。

  1. Grep を使用して、プロジェクトで NativeWind が使用されているかを検出します。

    • パターン:from ['"]nativewind['"](**/*.{ts,tsx,js,jsx} が対象)
    • package.json の依存関係に "nativewind" が含まれているかも確認します
    • NativeWind が検出された場合は、Phase 5 で使用するフラグ usesNativeWind = true を設定します
  2. Reanimated のバージョンを検出します(Phase 2 でデフォルト値を割り当てる際に必要です)。

    • package.json を読み込み、dependencies または devDependencies にある react-native-reanimated のバージョンを確認します
    • バージョンが ^4 または >=4.0.0 の場合は、reanimatedVersion = 4 を設定します
    • それ以外の場合は、reanimatedVersion = 3 を設定します(デフォルト値が同じ v2/v3 を対象とします)
  3. Grep を使用して、react-native-reanimated からインポートしているファイルをすべて検索します。

    • パターン:from ['"]react-native-reanimated['"]
    • **/*.{ts,tsx,js,jsx} を検索します
  4. Grep を使用して、React Native 組み込みの Animated API を使用しているファイルをすべて検索します。

    • パターン:from ['"]react-native['"] を含み、同時に Animated も使用しているファイル
    • パターン:Animated\.View|Animated\.Text|Animated\.Image|Animated\.Value|Animated\.timing|Animated\.spring
  5. Grep を使用して、すでに react-native-ease を使用しているファイルを検索します(再移行を避けるため)。

    • パターン:from ['"]react-native-ease['"]
  6. アニメーションコードを含む各ファイルを読み込みます。コンポーネントと、そのアニメーションパターンの一覧を作成します。

スキャン対象から除外するもの:

  • node_modules/
  • *.test.* および *.spec.* ファイル
  • ビルド出力ディレクトリ(lib/、build/、dist/)

Phase 2: 分類

検出した各コンポーネントを、移行可能または移行不可に分類します。

判定ツリー

以下のチェックを順番に適用します。最初に該当した項目で結果を決定します。

  1. ジェスチャー API を使用していますか?(Gesture.Pan、Gesture.Pinch、Gesture.Rotation、useAnimatedGestureHandler)→ 移行不可 — 「ジェスチャー駆動のアニメーション」
  2. スクロールハンドラーを使用していますか?(useAnimatedScrollHandler、Animated.event を使った onScroll)→ 移行不可 — 「スクロール駆動のアニメーション」
  3. 共有要素トランジションを使用していますか?(sharedTransitionTag)→ 移行不可 — 「共有要素トランジション」
  4. runOnUI またはワークレットディレクティブを使用していますか? → 移行不可 — 「ワークレットランタイムが必要」
  5. withSequence を使用していますか? → 移行不可 — 「アニメーションのシーケンスはサポートされていません」 5b. 単一のアニメーション(withTiming/withSpring)をラップする withDelay を使用していますか? → 移行可能 — トランジションの delay に割り当てます 5c. withSequence またはネストされた withDelay をラップする withDelay を使用していますか? → 移行不可 — 「複雑な遅延/シーケンスはサポートされていません」
  6. 複雑な interpolate() を使用していますか?(入力値または出力値が2つを超える)→ 移行不可 — 「複雑な補間」
  7. layout={...} プロパティを使用していますか? → 移行不可 — 「レイアウトアニメーション」
  8. サポートされていないプロパティをアニメーションしていますか?(以下以外のプロパティ:opacity、translateX、translateY、scale、scaleX、scaleY、rotate、rotateX、rotateY、borderRadius、backgroundColor、borderWidth、borderColor、shadowOpacity、shadowRadius、shadowColor、shadowOffset、elevation)→ 移行不可 — 「サポートされていないプロパティをアニメーションしています:<prop>」
  9. プロパティごとに異なるトランジション設定を使用していますか?(例:opacity に200msの timing、scale に spring を使用)→ 移行可能 — カテゴリーキー(transform、opacity、borderRadius、backgroundColor、border、shadow、default)を使った TransitionMap に割り当てます
  10. 状態によって駆動されていませんか?(アニメーションが React の状態ではなく、ジェスチャー/スクロール値によってトリガーされる)→ 移行不可 — 「状態駆動ではありません」
  11. 上記のいずれにも該当しない場合 → 移行可能
移行可能なパターンの対応付け

以下の表を使用して、Reanimated/Animated のパターンを EaseView に変換します。

Reanimated / Animated のパターンEaseView での同等の書き方
不透明度、移動、拡大縮小、回転、borderRadius、backgroundColor には useSharedValue + useAnimatedStyle + withTiminganimate={{ prop: value }} + transition={{ type: 'timing', duration, easing }}
withSpringtransition={{ type: 'spring', damping, stiffness, mass, velocity }}
entering={FadeIn} / FadeIn.duration(N)initialAnimate={{ opacity: 0 }} + animate={{ opacity: 1 }} + タイミングトランジション
entering={FadeInDown} / FadeInUpinitialAnimate={{ opacity: 0, translateY: ±value }} + animate={{ opacity: 1, translateY: 0 }}
entering={SlideInLeft} / SlideInRightinitialAnimate={{ translateX: ±value }} + animate={{ translateX: 0 }}
entering={SlideInUp} / SlideInDowninitialAnimate={{ translateY: ±value }} + animate={{ translateY: 0 }}
entering={ZoomIn}initialAnimate={{ scale: 0 }} + animate={{ scale: 1 }}
exiting={FadeOut} / その他の終了アニメーション状態駆動の終了:ブール値の状態 + アンマウントするための onTransitionEnd(レポートでは「状態変更が必要」としてフラグを付けます)
withRepeat(withTiming(...), -1, false)transition={{ type: 'timing', ..., loop: 'repeat' }} + 開始値用の initialAnimate
withRepeat(withTiming(...), -1, true)transition={{ type: 'timing', ..., loop: 'reverse' }} + 開始値用の initialAnimate
Easing.lineareasing: 'linear'
Easing.ease / Easing.inOut(Easing.ease)easing: 'easeInOut'
Easing.in(Easing.ease)easing: 'easeIn'
Easing.out(Easing.ease)easing: 'easeOut'
Easing.bezier(x1, y1, x2, y2)easing: [x1, y1, x2, y2]
Animated.Value + Animated.timing同じ animate + transition のパターン — 状態駆動に変換します
Animated.Value + Animated.springanimate + transition={{ type: 'spring' }} — 状態駆動に変換します
withDelay(ms, withTiming(...)) または withDelay(ms, withSpring(...))transition={{ ..., delay: ms }} — トランジション設定に delay を追加します
一時停止してから sv.value から再開する cancelAnimation(sv)paused プロパティ(同じ位置から再開します);一時停止時の値は onPaused から読み取ります
entering={FadeIn.delay(ms)} / .delay() を持つ任意の entering プリセットinitialAnimate + animate + transition={{ ..., delay: ms }}
useAnimatedStyle 内でプロパティごとに異なる withTiming / withSpringtransition={{ opacity: { type: 'timing', ... }, transform: { type: 'spring', ... } }}(プロパティごとのマップ)
デフォルト値のマッピング

重要:Reanimated と EaseView ではデフォルト値が異なります。元のアニメーション動作を維持するには、値を明示的に設定する必要があります。EaseView のデフォルト値が Reanimated のデフォルト値と一致すると想定しないでください。

Phase 1 の reanimatedVersion を使用して、適切なデフォルト値を選択してください。

withSpring → EaseView の spring

Reanimated v2/v3 のデフォルト値:

パラメーターReanimated v2/v3EaseView のデフォルト値対応
damping1015damping: 10 を設定する必要があります
stiffness100120stiffness: 100 を設定する必要があります
mass11同じ — 省略
velocity00同じ — 設定されている場合のみ引き継ぐ

Reanimated v4 のデフォルト値:

パラメーターReanimated v4EaseView のデフォルト値対応
damping12015damping: 120 を設定する必要があります
stiffness900120stiffness: 900 を設定する必要があります
mass41mass: 4 を設定する必要があります
velocity00同じ — 設定されている場合のみ引き継ぐ

Reanimated v4 では、デフォルトが臨界減衰の、キレのあるスプリング(バウンスなし)に変更されました。従来の物理ベースのデフォルト値は、開始条件と終了条件の影響を受けすぎることが変更の理由です。v4 では、生の物理パラメーターの代わりに duration + dampingRatio を使用することが推奨されています。

ソースコードでこれらの値が明示的に設定されている場合は、そのまま引き継いでください。明示的な値がなく、ソースが Reanimated のデフォルト値に依存している場合は、EaseView のトランジションに Reanimated のデフォルト値を明示的に設定してください。

例 — 設定なしの withSpring(1):

// Before (Reanimated)
scale.value = withSpring(1);

// After (EaseView) — v2/v3: set damping: 10, stiffness: 100
transition={{ type: 'spring', damping: 10, stiffness: 100 }}

// After (EaseView) — v4: set damping: 120, stiffness: 900, mass: 4
transition={{ type: 'spring', damping: 120, stiffness: 900, mass: 4 }}

duration ベースの spring: Reanimated v3 以降では、withSpring(target, { duration, dampingRatio }) もサポートされています。コードで dampingRatio/duration が明示的に設定されている場合は、次の式で変換してください:damping = dampingRatio * 2 * sqrt(stiffness * mass)。

withTiming → EaseView の timing
パラメーターReanimated のデフォルト値EaseView のデフォルト値対応
duration300300同じ — 省略
easingEasing.inOut(Easing.quad)'easeInOut'(cubic)easing: [0.455, 0.03, 0.515, 0.955] を設定する必要があります

イージングカーブは異なります。Reanimated のデフォルトは二次イーズインアウトですが、EaseView のデフォルトは三次です。ソースで指定されていない場合は、必ずイージングを明示的に設定してください。

例 — 設定なしの withTiming(1):

// Before (Reanimated)
opacity.value = withTiming(1);

// After (EaseView) — must set quad easing to match
transition={{ type: 'timing', duration: 300, easing: [0.455, 0.03, 0.515, 0.955] }}

ソースでイージングが明示的に設定されている場合は、上記のイージング表を使ってマッピングしてください。

Animated.timing(旧 RN API)→ EaseView の timing
パラメーターRN Animated のデフォルト値EaseView のデフォルト値対応
duration500300duration: 500 を設定する必要があります
easingEasing.inOut(Easing.ease)'easeInOut'同じカーブ — 省略
Animated.spring(旧 RN API)→ EaseView の spring

RN Animated では、デフォルトで friction/tension が使用されます:friction: 7, tension: 40。これらは次のように対応します:stiffness = tension、damping = friction。

パラメーターRN Animated のデフォルト値EaseView のデフォルト値対応
stiffness(tension)40120stiffness: 40 を設定する必要があります
damping(friction)715damping: 7 を設定する必要があります
mass11同じ — 省略
velocity00同じ — 設定されている場合のみ引き継ぐ

Animated.spring では、friction/tension の代わりに bounciness/speed も指定できます。これらは SpringConfig.fromBouncinessAndSpeed を通じて処理されるため、数値を直接マッピングせず、この関数で変換してください — デフォルトの bounciness: 0, speed: 12 は { stiffness: 342.1, damping: 36.93 } に変換されます。

単位の変換
  • 回転: Reanimated では変換に '45deg' 文字列を使用します → EaseView では 45(数値、度)を使用します。'deg' の接尾辞を削除して数値に解析してください。
  • 移動: どちらも DIP(密度非依存ピクセル)を使用します。変換は不要です。
  • スケール: どちらも単位のない倍率を使用します。変換は不要です。
  • スプリングの速度: アニメーション対象のプロパティと同じ単位を毎秒単位で使用します。移動は DIP、回転は度、スケールと不透明度は単位なしです。変換は不要ですが、速度は遷移ごとに設定するため、移動とスケールで共有する transform 遷移には、意味のある速度を 1 つしか設定できない点に注意してください。両方に速度が必要な場合は、カテゴリを分けてください。

フェーズ 3:ドライランレポート

コンポーネントの選択をユーザーに求める前に、必ずこのレポートを出力してください。このレポートはフェーズ 4 の前にユーザーが確認できる状態にする必要があります。

構造化されたレポートを出力してください。まだ変更は適用しないでください。

形式:

## Migration Report

### Summary
- Files scanned: X
- Components with animations: Y
- Migratable: Z  |  Not migratable: W

### Migratable Components

#### `path/to/file.tsx` — ComponentName
**Current:** Brief description of what the animation does and which API it uses
**Proposed:** What the EaseView equivalent looks like (include exact transition values with mapped defaults)
**Changes:** What will be added/removed/modified
**Note:** (only if applicable) "Requires state changes for exit animation" or other caveats

### Not Migratable (will be skipped)

#### `path/to/file.tsx` — ComponentName
**Reason:** Why it can't be migrated (from decision tree)

このレポートは、プラン内や折りたたまれた状態ではなく、会話内のテキスト出力として必ず表示してください。フェーズ 4 でコンポーネントを選択する前に、ユーザーが内容を確認する必要があります。


フェーズ 4:ユーザーによる確認

重要:ここでは必ず AskUserQuestion ツールを使用してください。プランモードやテキストプロンプトは使用せず、インラインで質問しないでください。AskUserQuestion ツールを直接呼び出してください。

次の正確なパラメーターを指定して AskUserQuestion を呼び出してください。

  • multiSelect:true
  • questions:次の内容を含む質問オブジェクトを 1 つ指定します。
    • header:"Migrate"
    • question:"Which components should be migrated to EaseView? All are selected — deselect any to skip."
    • multiSelect:true
    • options:移行可能なコンポーネントごとに 1 つのエントリーを指定します。各エントリーには次の内容を含めます。
      • label:コンポーネント名(例:"AnimatedButton")
      • description:ファイルパスとアニメーションの簡単な説明(例:"src/components/animated-button.tsx — spring scale on press")

移行可能なコンポーネントが 2 つある場合のツール呼び出し例:

{
  "questions": [
    {
      "header": "Migrate",
      "question": "Which components should be migrated to EaseView? All are selected — deselect any to skip.",
      "multiSelect": true,
      "options": [
        {
          "label": "AnimatedButton",
          "description": "src/components/simple/animated-button.tsx — spring scale on press"
        },
        {
          "label": "Collapsible",
          "description": "src/components/ui/collapsible.tsx — fade-in entering animation"
        }
      ]
    }
  ]
}

続行する前に、ユーザーの応答を待ってください。 プランモードに入らないでください。ユーザーがコンポーネントを選択する前に変更を適用しないでください。

ユーザーが何も選択しない場合、または「Other」を選択してキャンセルした場合は、「移行を中止しました。変更は行われていません。」として中止してください。

ユーザーが確認したコンポーネントのみを対象に、フェーズ 5 へ進んでください。


フェーズ 5:移行の適用

確認済みの各コンポーネントについて、移行を適用してください。

移行手順(コンポーネントごと)
  1. EaseView のインポートを追加(まだ存在しない場合):

    import { EaseView } from 'react-native-ease';
    

1b. usesNativeWind が true の場合、import 'react-native-ease/nativewind' がプロジェクト内にすでに存在するか確認します(すべてのファイルを検索)。存在しない場合は、アプリのルートエントリポイント(例:_layout.tsx、App.tsx、または index.tsx のうち、最も早いエントリ)に追加します。これはすべての移行を通じて一度だけ行えばよく、コンポーネントごとに行う必要はありません。

  1. アニメーションビューを置き換えます:

    • Animated.View → EaseView
    • <Animated.View style={[styles.box, animatedStyle]}> → <EaseView style={styles.box} animate={{ ... }} transition={{ ... }}>
  2. アニメーションフックをプロパティに変換します:

    • useSharedValue、useAnimatedStyle、withTiming、withSpring、withRepeat の呼び出しを削除します
    • それらの値を animate、initialAnimate、transition プロパティに変換します
  3. ** entering/exiting アニメーションを変換します:**

    • entering={FadeIn} → EaseView で initialAnimate={{ opacity: 0 }} + animate={{ opacity: 1 }}

    • exiting の場合:状態変数と onTransitionEnd コールバックを導入します:

      const [visible, setVisible] = useState(true);
      const [mounted, setMounted] = useState(true);
      
      // When triggering exit:
      setVisible(false);
      
      // On the EaseView:
      {
        mounted && (
          <EaseView
            animate={{ opacity: visible ? 1 : 0 }}
            transition={{ type: 'timing', duration: 300 }}
            onTransitionEnd={({ finished }) => {
              if (finished && !visible) setMounted(false);
            }}
          >
            ...
          </EaseView>
        );
      }
      
  4. インポートをクリーンアップします:

    • ファイル内で使用されなくなった Reanimated のインポートを削除します
    • 同じファイル内の未移行コードでまだ参照されている Reanimated のインポートは保持します
    • まだ使用されているインポートは決して削除しないでください
  5. 進捗を出力します:

    [1/N] Migrated ComponentName in path/to/file.tsx
    
安全ルール

これらのルールは絶対です。違反するとユーザーのコードが破損します。

  1. 迷ったらスキップします。 パターンが曖昧であるか、移行に自信がない場合は、「Not Migratable」に理由「Complex pattern — manual review recommended」を付けて追加します
  2. ファイル内の他の場所でまだ使用されているインポートは決して削除しないでください。 アニメーションコードを削除した後、削除する前に各インポートへの参照が残りのすべての行にないか確認します。
  3. アニメーション以外のロジックをすべて保持します。 イベントハンドラー、状態管理、エフェクト、コールバック — 移行対象のアニメーションに直接関係しない限り、一切触れないでください。
  4. コンポーネント構造と公開 API を保持します。 props、ref のフォワーディング、エクスポートされた型 — それらを同一に保ちます。
  5. 混在ファイルを正しく処理します。 ファイルに移行可能なアニメーションと移行不可能なアニメーションの両方がある場合は、安全なものだけを移行します。Reanimated のコードが残っている場合は、Reanimated のインポートを保持します。
  6. 回転の単位を正しくマッピングします。 Reanimated の '45deg' 文字列 → EaseView の 45 数値。ソースがラジアンを使用している場合は変換します:radians * (180 / Math.PI)。
  7. イージングプリセットを正しくマッピングします。 フェーズ 2 のマッピングテーブルを参照してください。
  8. TypeScript エラーを発生させないでください。 移行後にすべての型が正しいことを確認します。元のコードが型付き共有値を使用している場合は、EaseView のプロパティが一致することを確認します。

フェーズ 6:最終レポート

すべての移行が適用された後、出力します:

## Migration Complete

### Changed (X components)
- `path/to/file.tsx` — ComponentName: brief description of what was migrated

### Unchanged (Y components)
- `path/to/file.tsx` — ComponentName: reason skipped

### Next Steps
- Run your app and verify animations visually
- Run your test suite to check for regressions
- If no Reanimated code remains, consider removing `react-native-reanimated` from dependencies

EaseView API リファレンス(移行の正確性のため)

アニメーション可能なプロパティ

animate プロパティで指定できるすべてのプロパティ:

プロパティ型デフォルト値備考
opacitynumber10~1 の範囲
translateXnumber0DIPs(密度非依存ピクセル)単位
translateYnumber0DIPs 単位
scalenumber1scaleX と scaleY の省略形
scaleXnumber1X 軸の scale を上書き
scaleYnumber1Y 軸の scale を上書き
rotatenumber0Z 軸の回転角度(度)
rotateXnumber0X 軸の回転角度(3D)
rotateYnumber0Y 軸の回転角度(3D)
borderRadiusnumber0ピクセル単位
backgroundColorColorValue'transparent'任意の RN カラー値
borderWidthnumber0ピクセル単位
borderColorColorValue'black'任意の RN カラー値
shadowOpacitynumber00~1(iOS のみ)
shadowRadiusnumber0ピクセル単位(iOS のみ)
shadowColorColorValue'black'任意の RN カラー値(iOS のみ)
shadowOffsetobject{width:0,height:0}{ width, height }(iOS のみ)
elevationnumber0Android のマテリアルシャドウ
トランジションの種類

タイミング:

transition={{
  type: 'timing',
  duration: 300,        // ms, default 300
  easing: 'easeInOut',  // 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | [x1,y1,x2,y2]
  delay: 0,             // ms, default 0
  loop: 'repeat',       // 'repeat' | 'reverse' — requires initialAnimate
}}

スプリング:

transition={{
  type: 'spring',
  damping: 15,      // default 15
  stiffness: 120,   // default 120
  mass: 1,          // default 1
  velocity: 0,      // property units/s, default 0, native only
  delay: 0,         // ms, default 0
}}

なし(即時):

transition={{ type: 'none' }}
主な Props
  • animate — アニメーション対象プロパティの目標値
  • initialAnimate — 開始値(マウント時に animate の値までアニメーションします)
  • transition — アニメーション設定:単一の SingleTransition(timing/spring/none)、またはカテゴリキー(default、transform、opacity、borderRadius、backgroundColor、border、shadow)を持つ TransitionMap
  • onTransitionEnd — { finished: boolean } を受け取るコールバック
  • paused — 実行中のアニメーション(ループを含む)をその場で一時停止します。false にすると同じ位置から再開します
  • onPaused — paused が有効になった時点での animate のキーの画面上の値である { values } を受け取るコールバック
  • transformOrigin — { x: 0-1, y: 0-1 } で指定する回転・変形の基点。デフォルトは中央
  • useHardwareLayer — Android の GPU 最適化(ブール値、デフォルトは false)
  • className — NativeWind / Tailwind CSS のクラス文字列(プロジェクトに NativeWind が必要です)
重要な制約
  • ループには timing が必要です(spring は不可)。また、initialAnimate で開始値を定義する必要があります
  • プロパティごとのトランジションに対応 — カテゴリキー(default、transform、opacity、borderRadius、backgroundColor、border、shadow)を持つ TransitionMap を渡すと、プロパティグループごとに異なる設定を使用できます
  • アニメーションのシーケンスには非対応 — withSequence に相当する機能はありません。withDelay は delay トランジションプロパティで使用できます
  • ジェスチャーやスクロールに連動するアニメーションには非対応 — EaseView は状態駆動のみです
  • Style と animate の競合 — プロパティが style と animate の両方にある場合、アニメーション値が優先されます

コメント