emilkowal-animations-swift について
AppleプラットフォームのUI向けに、SwiftUIとAppKitのアニメーションを扱うリファレンススキルです。10カテゴリにわたる86のルールに加え、コードレビュー指針や実装値、Web概念との対応を示します。
主な機能
- 0110カテゴリ、86のルール
- 02アニメーションコードのレビュー項目
- 03時間とイージングの指針
- 04ジェスチャー、トランジション、スクロールのパターン
- 05アクセシビリティとパフォーマンスの指針
- 06WebとSwiftUIの概念対応
活用シーン
- 01SwiftUIアニメーション実装のレビュー
- 02UIトランジションの時間設定
- 03ジェスチャーに連動する動きやナビゲーションの構築
- 04アニメーションのアクセシビリティと性能の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ pluk-inc/ skills --skill emilkowal-animations-swiftインストール後、Claude Code で/emilkowal-animations-swiftを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.jsonassets/templates/_template.mdreferences/_sections.mdappkit-animator-proxy.mdappkit-core-animation.mdappkit-implicit-animations.mdappkit-layer-backed-views.mdappkit-nsanimation-context.mdappkit-spring-animation.mdease-avoid-linear.mdease-custom-timing-curve.mdease-ease-in-for-exits.mdease-ease-inout-for-emphasis.mdease-match-context.mdease-prefer-easeout.mdease-spring-damping.mdease-spring-default.mdease-spring-ios17.mdease-spring-response.mdgesture-asymmetric-press-release.mdgesture-cancellation.mdgesture-drag-basic.mdgesture-drag-constraints.mdgesture-drag-velocity.mdgesture-gesture-state.mdgesture-hover-macos.mdgesture-long-press.mdgesture-magnify-gesture.mdgesture-rotation-gesture.mdgesture-sensory-feedback.md詳細
SwiftUI と AppKit のアニメーションに関するベストプラクティス
Apple プラットフォームのインターフェース向けの包括的なアニメーションガイドです。Emil Kowalski のウェブアニメーションの原則と Framer Motion のベストプラクティスを応用しています。iOS 17 以降を対象に、影響度順に並べた 10 カテゴリー、86 のルールを収録しています。
レビュー形式(必須)
UI アニメーションのコードをレビューする際は、必ず Before/After/Why の列を含む Markdown テーブルを使用してください。「Before:」と「After:」を別々の行にしたリストは使用しないでください。次のような実際の Markdown テーブルを必ず出力してください。
| Before | After | Why |
|---|---|---|
.animation(.default) | .animation(.easeOut(duration: 0.2)) | イージングを明示し、デフォルトを避けます |
scaleEffect(0) | .opacity(0) を指定した scaleEffect(0.95) | 現実の世界では、何もないところから突然現れるものはありません |
ドロップダウンでの .easeIn | .easeOut(duration: 0.2) | easeIn は動きが鈍く感じられますが、easeOut は即座に反応します |
| ボタンを押したときのフィードバックがない | scaleEffect(isPressed ? 0.97 : 1.0) | ボタンは押したときに反応を感じられる必要があります |
ポップオーバーでの .scaleEffect(anchor: .center) | トリガーに合わせた .scaleEffect(anchor: .top) | ポップオーバーは中央ではなく、トリガーを基準に拡大する必要があります |
見つかった問題ごとに 1 行を設けてください。「Why」列では、理由を簡潔に説明します。
レビューチェックリスト
SwiftUI のアニメーションコードをレビューする際は、次の問題がないか確認してください。
| 問題 | 修正 |
|---|---|
.animation(.default) またはイージング曲線が明示されていない | 正確なイージングを指定します:.easeOut(duration: 0.2) または .spring(duration: 0.3) |
scaleEffect(0) での登場アニメーション | scaleEffect(0.95) と .opacity(0) から開始します |
登場する UI 要素での .easeIn | .easeOut または .spring() に切り替えます |
ポップオーバーでの .scaleEffect(anchor: .center) | アンカーをトリガーの位置に合わせます(モーダルは対象外です) |
| キーボード操作で開始されるアクションにアニメーションを適用している | アニメーションを完全に削除し、状態を即座に変更します |
| UI 要素の再生時間が 300ms を超えている | 150~250ms に短縮するか、スプリングを使用します |
#if os(macOS) によるガードのないホバーアニメーション | プラットフォームチェックまたは .onHover の利用可否に応じて制御します |
高頻度のアクションを withAnimation で囲んでいる | 削除します。1 日に 100 回以上実行されるアクションは即座に行います |
.frame() または .padding() をアニメーションしている | 代わりに .scaleEffect、.offset、.opacity を使用します(GPU で高速化されます) |
| 表示時と終了時のトランジション速度が同じ | 終了を速くします:.asymmetric(insertion: 0.3s, removal: 0.15s) |
| すべての要素が一度に表示される | 項目間に 30~80ms の遅延を加えます |
Timer/CADisplayLink を使った手動のアニメーションループ | .animation() または withAnimation を使用します(レンダーサーバー上で実行されます) |
accessibilityReduceMotion のチェックがない | @Environment(\.accessibilityReduceMotion) によるガードを追加します |
| タップ可能な要素に押下時のフィードバックがない | ButtonStyle を介して、押下時に scaleEffect(0.97) を追加します |
適用する場面
次の場面では、これらのガイドラインを参照してください。
- SwiftUI ビューにアニメーションを追加するとき
- イージング曲線、スプリング、タイミングの値を選ぶとき
- ジェスチャーに基づく操作(ドラッグ、タップ、長押し)を実装するとき
- トランジションやナビゲーションアニメーションを作成するとき
- 共有要素のトランジションに
matchedGeometryEffectを使用するとき - スクロール連動エフェクトや視差効果を作成するとき
- iOS 17 以降の
PhaseAnimatorまたはKeyframeAnimatorを使用するとき - アニメーションのパフォーマンスを最適化するとき
accessibilityReduceMotionを使用してアニメーションのアクセシビリティを確保するとき- AppKit/macOS 固有のアニメーションを記述するとき
優先度別ルールカテゴリー
| 優先度 | カテゴリー | 影響度 | 接頭辞 |
|---|---|---|---|
| 1 | タイミング曲線とイージング | CRITICAL | ease- |
| 2 | 再生時間とタイミング | CRITICAL | timing- |
| 3 | アニメーションプロパティ | HIGH | props- |
| 4 | 変形とエフェクト | HIGH | transform- |
| 5 | ジェスチャーと操作 | HIGH | gesture- |
| 6 | トランジションとナビゲーション | MEDIUM-HIGH | transition- |
| 7 | スクロールと視差効果 | MEDIUM | scroll- |
| 8 | 戦略的なアニメーション | MEDIUM | strategy- |
| 9 | アクセシビリティと仕上げ | MEDIUM | polish- |
| 10 | AppKit 固有 | LOW-MEDIUM | appkit- |
クイックリファレンス
1. タイミングカーブとイージング(重大)
ease-prefer-easeout- デフォルトのイージングには easeOut を使用しますease-avoid-linear- UI アニメーションでは線形イージングを避けますease-spring-default- 自然な動きにはスプリングを優先しますease-spring-ios17- bounce と duration を指定できる iOS 17 の Spring 構造体を使用しますease-custom-timing-curve- timingCurve でカスタムカーブを作成しますease-ease-in-for-exits- 終了アニメーションには easeIn を使用しますease-ease-inout-for-emphasis- 強調する動きには easeInOut を使用しますease-spring-response- 持続時間の印象を調整するには、スプリングの response を設定しますease-spring-damping- バウンスを調整するには、スプリングの damping を設定しますease-match-context- アニメーションの状況に合わせてイージングを選びます
2. 持続時間とタイミング(重大)
timing-200ms-default- UI アニメーションのデフォルトの持続時間には 200ms を使用しますtiming-100ms-micro- マイクロインタラクションには 100ms を使用しますtiming-300ms-emphasis- 強調アニメーションは 300ms 未満にしますtiming-stagger-children- 子要素のアニメーションをずらして連携させますtiming-delay-strategic- 順序付けには遅延を戦略的に使用します
3. アニメーションのプロパティ(高)
props-opacity-scale- パフォーマンスを高めるには、不透明度とスケールを優先しますprops-avoid-size-animate- フレームサイズを直接アニメーション化するのは避けますprops-transform-origin- 変形のアンカーポイントを設定しますprops-drawing-group- 複雑な階層には drawingGroup を使用しますprops-animation-disable- アニメーションを無効にするには animation(nil) を使用しますprops-explicit-animation- 明示的に制御するには withAnimation を使用しますprops-implicit-animation- 暗黙的なアニメーションには .animation モディファイアを使用します
4. 変形とエフェクト(高)
transform-scale-subtle- 控えめなスケール値(0.95〜0.98)を使用しますtransform-rotation-purposeful- 意図を持って回転を適用しますtransform-translate-direction- 意味のある方向へ移動させますtransform-3d-perspective- 奥行きを表現するには rotation3DEffect を使用しますtransform-order-matters- モディファイアの順序は出力に影響しますtransform-anchor-point- 回転やスケールの原点となるアンカーを設定しますtransform-combine-effects- 変形を意図的に組み合わせますtransform-blur-crossfade- 不完全なクロスフェードを目立たなくするには、ぼかしを使用します
5. ジェスチャーとインタラクション(高)
gesture-tap-feedback- タップに即座にフィードバックを返しますgesture-long-press- 長押しの状態をアニメーションしますgesture-drag-basic- 滑らかなドラッグ操作を実装しますgesture-drag-constraints- ドラッグを範囲内に制限しますgesture-drag-velocity- 速度を利用して慣性効果を実現しますgesture-gesture-state- 一時的な値には @GestureState を使用しますgesture-updating-modifier- リアルタイムのフィードバックには .updating を使用しますgesture-simultaneous-vs-exclusive- ジェスチャーの組み合わせ方を選択しますgesture-magnify-gesture- ピンチによる拡大・縮小を実装しますgesture-rotation-gesture- 回転ジェスチャーを実装しますgesture-hover-macos- macOS でホバーを処理しますgesture-sensory-feedback- ジェスチャーに触覚フィードバックを組み合わせますgesture-spring-on-release- ジェスチャー終了時にスプリングで元に戻しますgesture-cancellation- ジェスチャーのキャンセルを適切に処理しますgesture-asymmetric-press-release- 押下と解放のタイミングを非対称にします
6. トランジションとナビゲーション(中〜高)
transition-builtin-types- 組み込みのトランジションタイプを使用しますtransition-asymmetric- 入場と退出で異なる動作にするために asymmetric を使用しますtransition-combined- トランジションを組み合わせて、より豊かな効果を実現しますtransition-custom-modifier- カスタムのトランジションモディファイアを作成しますtransition-matched-geometry- matchedGeometryEffect を適切に使用しますtransition-namespace-scope- @Namespace のライフサイクルを管理しますtransition-navigation-transitions- ナビゲーションのトランジションをカスタマイズしますtransition-sheet-presentations- シートの表示をアニメーションしますtransition-id-for-replacement- ビューの置き換えに .id() を使用しますtransition-content-transition- テキストには contentTransition を使用します
7. スクロールと視差(中)
scroll-geometry-reader- GeometryReader でスクロール位置を取得しますscroll-preference-key- スクロールデータには PreferenceKey を使用しますscroll-parallax-effect- 視差スクロール効果を作成しますscroll-sticky-header- アニメーションする固定ヘッダーを実装しますscroll-scroll-transition- iOS 17 の scrollTransition モディファイアを使用しますscroll-visual-effect- iOS 17 の visualEffect モディファイアを使用します
8. 戦略的なアニメーション(中)
strategy-purposeful-motion- すべてのアニメーションに目的を持たせるstrategy-hierarchy-emphasis- 動きで階層を示すstrategy-state-communication- 状態の変化を明確にアニメーション表示するstrategy-spatial-continuity- 空間的な関係を維持するstrategy-brand-expression- 動きでブランドを表現するstrategy-frequency-framework- アニメーションを付ける前に使用頻度を基準に判断するstrategy-no-keyboard-animation- キーボードで開始されたアクションはアニメーション表示しないstrategy-component-design- 愛されるライブラリのためのコンポーネント設計原則
9. アクセシビリティと仕上げ(中)
polish-reduce-motion- accessibilityReduceMotion を尊重するpolish-phase-animator- シーケンスには PhaseAnimator を使用するpolish-keyframe-animator- 複雑な軌跡には KeyframeAnimator を使用するpolish-animation-completions- アニメーションの完了を処理するpolish-interruptible-animations- アニメーションを中断可能にするpolish-animation-debugging- アニメーションを効果的にデバッグするpolish-performance-profiling- アニメーションのパフォーマンスをプロファイリングするpolish-symbol-effects- SF Symbol のエフェクトを使用するpolish-text-animations- contentTransition でテキストをアニメーション表示するpolish-haptic-pairing- アニメーションと触覚フィードバックを組み合わせるpolish-skip-subsequent-delays- 以降のツールチップやポップオーバーでは遅延を省略する
10. AppKit 固有(低〜中)
appkit-nsanimation-context- グループ化には NSAnimationContext を使用するappkit-core-animation- Core Animation レイヤーを使用するappkit-layer-backed-views- パフォーマンス向上のためにレイヤーバッキングを有効にするappkit-implicit-animations- 暗黙的なレイヤーアニメーションを活用するappkit-spring-animation- AppKit でスプリングアニメーションを作成するappkit-animator-proxy- ビューのアニメーションには animator プロキシを使用する
主な値の一覧
| 値 | 用途 |
|---|---|
.spring(duration: 0.3, bounce: 0.2) | iOS 17 の標準的なスプリングアニメーション |
.spring(response: 0.3, dampingFraction: 0.7) | 従来のスプリング設定 |
.easeOut(duration: 0.2) | 標準的な UI トランジション |
scaleEffect(0.97) | ボタンを押したときのフィードバック |
scaleEffect(0.95) | 最小の開始時スケール(0 にはしない) |
0.2 秒 | マイクロインタラクションのデフォルト時間 |
0.3 秒 | UI アニメーションの最大時間 |
0.5 秒 | シート/ドロワーのアニメーション時間 |
コンセプトの対応関係(Web から SwiftUI へ)
| Web のコンセプト | SwiftUI での相当機能 |
|---|---|
ease-out | .easeOut または Animation.easeOut(duration:) |
cubic-bezier(a,b,c,d) | .timingCurve(a, b, c, d, duration:) |
| Framer Motion のスプリング | .spring(duration:bounce:)(iOS 17 以降) |
transform: scale(0.97) | .scaleEffect(0.97) |
transform-origin | .scaleEffect(anchor: .topLeading) |
useMotionValue | @State / @GestureState |
AnimatePresence | .transition() + .id() |
layoutId | .matchedGeometryEffect(id:in:) |
useScroll | GeometryReader + PreferenceKey |
whileHover/whileTap | .onHover {} / ジェスチャーモディファイア |
dragConstraints | 境界値を指定した onChange を使う DragGesture |
prefers-reduced-motion | @Environment(\.accessibilityReduceMotion) |
参照ファイル
| ファイル | 説明 |
|---|---|
| references/_sections.md | カテゴリーの定義と並び順 |
| assets/templates/_template.md | 新しいルール用のテンプレート |
| metadata.json | バージョンと参照情報 |
コメント
Markdown 対応