VYBE Motion Design
動きはメッセージです。すべての動きは、主題について何かを語らなければなりません——重さ、速さ、自信、驚き——さもなければ存在すべきではありません。
すべてのレシピはHyperFrames用です:コンポジションごとに一時停止されたGSAPタイムライン、決定的、シーク可能。エントランスにはfromTo、アニメーションにはtransforms + opacity + clipPath + filterを使用し、レイアウトプロパティは使用しません。
リファレンス(必要なものをロードしてください)
| ファイル | 用途 |
|---|---|
references/animation-principles.md | UI/タイポ/グラフィックに適用された12の原則、イージング用語集、タイミングテーブル |
references/transitions.md | GSAPコード付き14のトランジションレシピ、それぞれの使用タイミング |
references/camera-movements.md | ドリー、トラック、クレーン、オービット、ズームスルー、ウィップ、ラックフォーカス、パララックス — 2Dと3D |
references/visual-effects.md | グロー、ブルーム、ライトリーク、フィルムグレイン、クロマティックスプリット、モーションブラー、マスク、モーフ |
10のルール
- アニメーションさせるのではなく、振り付けせよ。 誰が最初に動き、誰が応答し、誰がホールドするかを計画する。 シーンは文である:主語 → 述語 → 目的語。
- イージングは個性である。
expo.out= 高級感/自信;back.out= 遊び心;power2.inOut= 機械的なカメラ;sine.inOut= 息づかい/アンビエント;none= 一定のドリフト。作品ごとに2〜3つのイージングを選び、一貫して使用する。 - 速く入り、ゆっくり落ち着く。 ほとんどのエントランス:0.5〜0.9秒で、移動の80%を 時間の最初の30%で完了する。
- **退場はエントランスより速く(約60%)、
.inイージングを使用する。 - スタッガーは階層を明らかにする — 文字は0.04〜0.08秒、単語は0.1〜0.18秒、 リスト項目は0.3〜0.6秒。すべてを同じようにスタッガーさせてはいけない。
- アクションを重ねる。 次の要素は前の要素が終了する前に開始する
(位置パラメータ
"<0.2"/ 絶対時間)。連続 = ロボット的。 - 読むためのホールド。 単語あたり約0.3秒 + 0.5秒; 閉じ行は2秒以上。
- 常に何かを生きた状態に保つ。 ホールド中、ゆっくりとしたプッシュ(
scale 1→1.04)、ドリフト またはライトスイープでフレームが死ぬのを防ぐ——ただしレイヤーごとにアンビエントモーションは1つだけ。 - トランジションを変化させる。 作品ごとにブラックフェードスルーは最大1回。 動きのカット、マッチカット、マスクワイプ、ズームスルー、共有要素を優先する。
- ビートを尊重する。 音楽がある場合、すべての主要な変化はビート(
60/bpm)に合わせる; ヒットはダウンビートに合わせ、アンティシペーションはその前に開始する。
シーク可能なGSAPパターン
const tl = gsap.timeline({ paused: true });
// エントランス: ぼかしからシャープへの上昇(プレミアム)
tl.fromTo("#s1-title", { opacity: 0, y: 40, filter: "blur(12px)" },
{ opacity: 1, y: 0, filter: "blur(0px)", duration: 0.9, ease: "expo.out" }, 0.2);
// マスクワイプリビール(エディトリアル)
tl.fromTo("#s1-line", { clipPath: "inset(0 100% 0 0)" }, { clipPath: "inset(0 0% 0 0)", duration: 0.8, ease: "expo.inOut" }, 0.5);
// グリフごとの広がり(letterSpacingの代わり — lintが拒否するため)
gsap.utils.toArray("#s1-word .ch").forEach((c, i, all) => {
const off = (i - (all.length - 1) / 2) * -30;
tl.fromTo(c, { x: off, opacity: 0 }, { x: 0, opacity: 1, duration: 1, ease: "expo.out" }, 1 + Math.abs(i - (all.length - 1) / 2) * 0.05);
});
// カウントアップ(シーク可能: onUpdateはシークのたびに実行)
const n = { v: 0 }, el = document.getElementById("s1-num");
tl.fromTo(n, { v: 0 }, { v: 97, duration: 1.4, ease: "expo.out", onUpdate: () => (el.textContent = Math.round(n.v)) }, 1.2);
// アンビエントホールド
tl.fromTo("#s1-stack", { scale: 1 }, { scale: 1.04, duration: 3, ease: "sine.inOut" }, 0);
window.__timelines["s1"] = tl;
テキスト分割:単語/文字を <span class="w|ch" style="display:inline-block">
でラップする(transformsはインライン要素では効かない)。スペースはスパンの外側に保つ。
引き渡し前の確認
- 連続する2つのシーンで同じエントランスモーションを使用しないこと。
- 同じ要素の同じプロパティを重複する時間でトゥイーンしないこと。
npx hyperframes checkMotionセクションがクリーンであること;振り付け監査には、インストール済みの場合、HyperFramesの/hyperframes-animationスキルに同梱されているanimation-map.mjsスクリプトを実行すること。