モーションデザイン原則 は何をしますか?
これは「味付け」のレイヤーです。動きがどのように振る舞うべきかを判断し、洗練され、意図的に見えるようにします。ジャンキー、フワフワ、または安っぽく見えるのを防ぎます。このスキルはモーションを再生するコードを書きません。カーブ、タイミング、アークを決定したら、その判断はディレクション/レンダリングスキル(プロダクトビデオの場合は product-demo-director)やCSS/JSトランジション(UIの場合)への入力となります。2つの役割は分けて考えてください。このスキルは判断を担当し、ディレクションスキルはオーケストレーションとメカニクスを担当します。
モーションの「感触」に関するあらゆる質問に反応します。「これをジャンキーじゃなくして」「もっとスムーズに」「もっと洗練された感じに」「もっと生き生きと」「イージングが変」「安っぽく見える」「イーズインかイーズアウトか」「スプリングかインターポレートか」
代わりに兄弟スキルを使うべき場合
このスキルはカーブ/タイミング/アークの判断のみを行います。実際の問題が以下の場合は、他のスキルに委譲してください:
- ショットの構成、シーン順序、ビートリスト → video-storyboard
- プロダクトデモ全体のディレクション(カーソル移動、スクリーンキャプチャ、機能コールアウトなど、エンドツーエンドのシーン構成) → product-demo-director(このスキルはそちらが消費するモーション仕様を生成します)
- テキストや文字のアニメーション(飛び込む、構築、表示など) → kinetic-typography
- ビート、スコア、効果音に合わせたタイミング → sound-and-music-sync
- プラットフォームに合わせたクロッピングやリフレーミング(9:16、1:1、セーフエリア) → social-video-formatter
- ライト、露出、グロー、カラーモーション → motion-color-and-light
操作手順
処方する前に診断せよ。「ジャンキー」なモーションのほとんどは4つの名前付き問題のいずれかであり、それぞれに特定の修正方法があります。以下の手順を順番に実行してください。タイミングが間違っていればイージングの修正は無意味であり、要素にアークがなければタイミングの修正も無意味です。
ステップ1:問題を特定する
モーション(またはその説明)を観察し、分類します。ほとんどの不満は以下のいずれかに該当します:
- リニア/ロボット的 — 一定速度で動き、停止時も急に止まる。イージングなし。ステップ2で修正。
- フワフワ/モッサリ — 遅すぎる、またはスナップすべきところでイーズインとイーズアウトの両方を使っている。持続時間またはカーブが間違っている。ステップ2とステップ3で修正。
- ジャンキー/カクカク — 出入りが同じ速度、またはすべてのプロパティがオフセットなしで同じフラットなタイムラインでアニメーションする。スペーシングとアークが不足。ステップ3とステップ4で修正。
- 死んでいる/活気がない — 技術的には正しいが退屈:予備動作なし、オーバーシュートなし、フォロースルーなし。アークと二次モーションが不足。ステップ4とステップ5で修正。
何かを触る前に、診断結果を一行で書き留めてください。「モーダルがフワフワしているのは、600msのイーズインアウトでフェードしているから」は修正可能ですが、「良くして」は修正できません。
ステップ2:イージングカーブを選ぶ
イージングはモーションの速度-時間グラフの形状です。カーブは知覚品質のほとんどを担います。要素の動作に合わせてカーブを選択します:
- イーズアウト(速い開始、遅い着地) — 要素が入ってくる場合およびユーザーがトリガーしたもの全般のデフォルト。モーションがすぐに始まり、その後減速して所定の位置に落ち着くため、レスポンシブに感じられます。使用例:要素の出現、ドロップダウンの展開、タップしたボタンの応答、コンテンツのスライドイン。迷ったらイーズアウトを選びましょう。
- イーズイン(遅い開始、速い終了) — 要素が出ていく場合。要素が加速しながら画面外に消えます。単独で入場に使うと遅くてラグい感じになり、「フワフワ」の最大の原因です。使用例:破棄、閉じる、退出。
- イーズインアウト(遅い、速い、遅い) — 画面上の既存の要素をAからBへ移動させる場合:カードの並び替え、カルーセルの進行、トグルのスライド。両端が固定されているため、両端をイージングするとコントロールされた印象になります。入場には使わないでください。遅い開始がレスポンシブさを殺します。
- スプリング(物理:質量、剛性、減衰 — オーバーシュートして落ち着く) — 生き生きとした物理的な感じにしたいモーションに:ポップインするモーダル、トグルノブ、いいねボタン、通知のドロップイン。軽いオーバーシュートが「生き生きとした」印象を与えます。減衰で個性を調整:低減衰 = よりバウンシーで遊び心、高減衰 = よりタイトでコーポレート。
spring() vs interpolate() の判断(コードでそれぞれを使うタイミング)については、品質基準と以下に示す実行可能アーティファクトを参照してください。簡単に言うと、物理的/生き生きとしたトランスフォームにはスプリング、不透明度フェードやカラー、プログレスバーなどのフレーム単位の精密な値にはインターポレートを使用します。
ステップ3:タイミングとスペーシングを設定する
タイミングは全体の持続時間です。スペーシングはその持続時間全体に距離がどのように分布するかです。スペーシングはイージングを具現化したものです(イーズアウトはフレームが最後に集中します)。2つのルール:
- 持続時間は距離とサイズに比例し、好みには比例しない。 小さな要素が短い距離を移動する場合は速く(UI:約150〜250ms)。大きな要素や長い移動距離の場合はより長く(約300〜500ms)しないとテレポートしたように見えます。ビデオ内の大きな全画面/ヒーローモーションはさらに長くても構いません。同じモーションでも距離が長ければ持続時間も長く。
- 入場と退場は対称ではない。 退場は入場より速くすべき(約0.7〜0.8倍)。ユーザーはその要素をもう必要としないので、早く片付けます。入退場の持続時間が同じだと、「ジャンキー」の古典的な原因になります。
プロダクトビデオの場合は、このスキルを離れる時点でフレーム数に変換します:frames = round(seconds * fps)。これらのフレーム数は、product-demo-directorに渡すリテラルな入力になります(product-demo-directorはそれらをタイミング付きのシーンプランに変換します)。
ステップ4:予備動作→アクション→フォロースルーのアークを構築する
現実のモーションが単一の動きであることは稀です。最も豊かで「生き生きとした」モーションには3つのビートがあります:
- 予備動作 — メインの動きの前に、反対方向への小さな巻き戻し動作(ボタンがポップする前に少し内側に沈み込む、カードがスライドアウトする前に少し後退する)。眼球を準備させ、動作を意図的に見せます。小さく速く保ちます。大きすぎるとグリッチのように見えます。
- アクション — メインの動き。ステップ2のイージングとステップ3の持続時間を適用します。スペーシングがここに存在します。
- フォロースルー — 要素が目標を少し超えてから戻る(スプリングのオーバーシュート)、および付随/二次要素がリーダーにわずかに遅れて追従する(ラベルが載っているカードに追いつく)。これが重みと生命感を演出します。
すべてのマイクロインタラクションに3つすべてが必要なわけではありません。150msのツールチップはアクションだけで十分です。しかし、何かが「死んでいる」と感じるときは、ほとんどの場合、予備動作かフォロースルーが欠けています。装飾を追加する前にアークを追加してください。
ステップ5:関連するディズニー原則を適用する
ディズニーのアニメーション12原則は、UIとプロダクトモーションに直接対応します。12すべてを適用する必要はありません。診断された問題を修正するために必要な数個だけを使用します。完全な対応表は references/twelve-principles にあります。効果の高いもの:
- スローイン&スローアウト → イージング(ステップ2)。単一最大の品質向上レバー。
- 予備動作 → 巻き戻し(ステップ4)。
- フォロースルー&オーバーラップアクション → オーバーシュート+遅延する二次要素(ステップ4)。「正しい」と「生き生きとした」の違い。
- タイミング → 持続時間とスペーシング(ステップ3)。
- スクワッシュ&ストレッチ → 押下/衝撃時の微妙なスケール変化で質量を示す。UIでは数パーセント以内に抑えないとおもちゃのように見えます。
- ステージング → 一度に一つのものを動かし、目線を誘導する。6つのものを同じカーブで同時にアニメーションさせない。
- 二次アクション → メインの動きを強化する小さなサポートモーション(カードが持ち上がるにつれて影が濃くなるなど)。競合しないように。
- 誇張 → キーポーズを現実より少しだけ誇張する。UIでは控えめに、インパクトのあるプロダクトビデオではより自由に。
ステップ6:仕様を引き渡す
以下のモーション仕様テンプレートを埋めてください。カーブ、持続時間(およびフレーム数)、アークのビート、プロパティごとのスプリングvsインターポレートの判断。この埋められた仕様が引き渡しアーティファクトです:プロダクトビデオシーンの場合は直接 product-demo-director に渡し、UIの場合はトランジション/キーフレームの値になります。シーンに光/露出のモーションも必要な場合は motion-color-and-light と組み合わせ、テキストがアニメーションする場合は kinetic-typography と、タイミングをビートに合わせる必要がある場合は sound-and-music-sync と組み合わせてください。
品質基準
モーションがA+となるのは、以下のすべてが満たされた場合のみです:
- 診断が明確にラベル付けされ(リニア/フワフワ/ジャンキー/死んでいる)、すべての変更がそこにトレースできる。「理由なく良くした」は禁止。
- 入場はイーズアウト、退場はイーズイン、画面上のA→Bはイーズインアウト、生き生き/物理的にはスプリングを使用。 意図的な一定速度ループ(スピナー、マーキー)以外では、生のリニアモーションは出荷されない。
- 持続時間は距離/サイズに比例し、退場は入場より速い。
spring()vsinterpolate()の判断がプロパティごとに明示的である:物理的なトランスフォーム(スケール、トランスレート、ポップ/スライド/セトル — オーバーシュートが必要)にはスプリング;精密なタイミング値(不透明度フェード、カラー、プログレス、オーバーシュートがおかしく見えるもの)にはインターポレート。スプリングで駆動されるフェード、またはリニアインターポレートで駆動されるロゴポップは欠陥です。- ヒーロー/シグネチャーモーションには、予備動作またはフォロースルーの少なくとも一方が存在する。活気のないモーションは、まさにこれらが欠けている。
- 一度に一つのものがリードする(ステージング)。プロパティはすべてが1つのフラットなタイムラインで発火するのではなく、オフセットされている。
- カーブ、持続時間、フレーム数が書き留められており、product-demo-directorまたはトランジションに渡す準備ができている。このスキルは判断を暗黙のままにしない。
やってはいけないこと
- 入場や退場にリラインージングを出荷してはいけない。これはロボット的なデフォルトであり、未完成に見える。一定速度は意図的なループのみに使用する。
- 入場にイーズイン、退場にイーズアウトを使ってはいけない。それはフワフワ/ラグい感じの逆である。入場はイーズアウト、退場はイーズイン。
- 入場と退場に同じ持続時間を使ってはいけない。退場は約0.7〜0.8倍速くする。
- フェード、カラー、プログレスバーをスプリングで駆動してはいけない。オーバーシュートにより値が範囲を超えてフラッシュする。タイミング値にはインターポレートを使用する。
- 生き生きとしたポップ/スライド/スケールを、生き生きと感じさせたい場合にリニアインターポレートで駆動してはいけない。オーバーシュートのためにスプリングを使用する。
- 多くのプロパティを1つのフラットなタイムラインでアニメーションさせてはいけない。ステージングし、オフセットする。
- UIで予備動作やオーバーシュートをやりすぎてはいけない。数ピクセル/数パーセントに抑える。大きな巻き戻しやバウンスはグリッチやおもちゃのように見える。
- ここでショットの順序を計画したりデモを組み立てたりしてはいけない。仕様を product-demo-director に渡す。ストーリーボードの順序は video-storyboard の担当。
イージング&スプリングピッカー
自己完結型のNodeスクリプト。motion_pick.jsとして保存し、node motion_pick.jsで実行してください。アニメーションさせる要素に合わせて requests 配列を編集すると、カーブ、距離に応じた持続時間(ミリ秒とフレーム)、退場持続時間、スプリングvsインターポレートの判断が返されます。依存関係はありません。
// モーションピッカー。`requests` を編集し、次に: node motion_pick.js
const fps = 30 // プロダクトビデオ用。UIはフレーム列を無視
// role: 'enter' | 'exit' | 'move' | 'lively'
// distancePx: 移動距離(持続時間を決定)。sizePx: 要素の大まかなサイズ。
const requests = [
{ name: 'モーダルポップイン', role: 'lively', property: 'scale', distancePx: 0, sizePx: 600 },
{ name: 'モーダル背景', role: 'enter', property: 'opacity', distancePx: 0, sizePx: 1920 },
{ name: 'トーストスライドイン', role: 'enter', property: 'translate', distancePx: 320, sizePx: 360 },
{ name: 'トースト破棄', role: 'exit', property: 'translate', distancePx: 320, sizePx: 360 },
{ name: 'カード並び替え', role: 'move', property: 'translate', distancePx: 140, sizePx: 280 },
{ name: 'プログレスフィル', role: 'move', property: 'width', distancePx: 800, sizePx: 800 },
]
function curveFor(role) {
if (role === 'enter') return 'ease-out'
if (role === 'exit') return 'ease-in'
if (role === 'move') return 'ease-in-out'
return 'spring' // lively
}
// 持続時間は移動距離と要素サイズに比例。下限+傾き、クランプあり。
function durationMs(role, distancePx, sizePx) {
const reach = Math.max(distancePx, sizePx * 0.4)
let ms = 140 + reach * 0.45
ms = Math.min(Math.max(ms, 140), 600)
if (role === 'exit') ms *= 0.75 // 退場はより速く
return Math.round(ms)
}
// 物理的なトランスフォームにはスプリング、タイミング値にはインターポレート。
function driver(role, property) {
const physical = property === 'scale' || property === 'translate'
if (role === 'lively') return 'spring() // オーバーシュートが必要'
if (physical && role !== 'move') return 'spring() // 物理的、生き生きとした着地'
return 'interpolate() // 精密、フレーム単位のタイミング'
}
const pad = (s, n) => String(s).padEnd(n)
console.log(pad('要素', 18), pad('カーブ', 12), pad('ミリ秒', 6), pad('フレーム', 8), 'ドライバー')
for (const r of requests) {
const curve = curveFor(r.role)
const ms = durationMs(r.role, r.distancePx, r.sizePx)
const frames = Math.round((ms / 1000) * fps)
console.log(pad(r.name, 18), pad(curve, 12), pad(ms, 6), pad(frames, 8), driver(r.role, r.property))
}
モーション仕様テンプレート
このスキルを離れるとき、各要素について以下のテンプレートに記入し、出力すること。プロダクトビデオの場合は、ミリ秒を product-demo-director 用のフレーム数に変換する。
要素名: <名前>
カーブ: <ease-out | ease-in | ease-in-out | spring>
持続時間: <ミリ秒>(プロダクトビデオ:<フレーム数>)
退場持続時間: <ミリ秒>(入場の0.75倍である必要がある)
プロパティドライバー:
scale: spring() # 物理的/生き生きとしたもの
translate: spring() # 物理的/生き生きとしたもの
opacity: interpolate() # タイミング値
color: interpolate() # タイミング値
width: interpolate() # タイミング値
アークビート: <予備動作のみ | アクションのみ | 予備動作+アクション+フォロースルー>
プロンプト例
「モーダルがフワフワしている」
このユーザーは品質の問題を感じているが言葉にできない。診断を導き、仕様に記入せよ。
診断: フワフワ — イージングが間違っている可能性が高い。
デフォルトのモーダルポップインと背景には以下を使用:
- 背景: イーズアウト、200ms、インターポレート(不透明度はタイミング値のため)
- モーダル: スプリング(剛性180、減衰18)、〜300ms(約9フレーム@30fps)、スケールはスプリングを使用
- 退場: 両方ともイーズイン、背景150ms、モーダル225ms
- アークビート: 予備動作+アクション+フォロースルー(予備動作として-2%スケールで沈み込むバンプ)
この仕様をproduct-demo-director(ビデオの場合)またはトランジションクラス(UIの場合)の入力として。
「もっと生き生きとした感じに」
問題は死んでいる/活気がない。アークと二次アクションを追加せよ。
診断: 死んでいる — 予備動作またはフォロースルーが不足。
既存のカーブは維持しつつ以下を追加:
- オーバーシュートを与えるスプリング(現在イーズアウトの場合)
- 予備動作:目標の95%から開始(50msでスケール1.0に)
- フォロースルー:短い追加トランスレート(5px、80msで解決)
- 二次アクション:モーダルが上がるにつれて影を深く、不透明度を0.2→0.6(インターポレート)
「これをスムーズにして」
ユーザーは特定できないが実際の診断はジャンキーである可能性が高い。ステップ1、3、4を進めよ。
診断を求めよう:「今の動きを説明してもらえますか?一定速度ですか?すべてが同時に動きますか?」
"Everything moves at same speed" → ジャンキー。
修正:
- 入場にイージングを追加(デフォルトでイーズアウト)
- プロパティをオフセット:不透明度が最初(0ms)、トランスレートが50ms遅れ、スケールが100ms遅れ
- 各プロパティの持続時間を短縮:不透明度180ms、トランスレート220ms、スケール260ms(スプリング付き)
- product-demo-directorがあれば、これらはオフセットタイムラインの入力になる
このスキルの出力は常にモーション仕様であり、コードではない。仕様を書き、次に正しいディレクションスキルを呼び出すか、UIのトランジションを実装せよ。