Communitygithub.com

shubham001312/scroll-motion-graphics

スクロール位置で駆動するモーション・グラフィックスセクション(Canvasフィルム、ピン留めストーリーテリング、スクロール連動チャプター、3Dドラムなど)の構築・修正に使用します。既存のスクロールアニメーションがカクつく、ホイールを乗っ取る、テキストの下に描画される、CPUを無駄に消費する、またはprefers-reduced-motionを無視する場合にも使用します。単純なスクロール連動フェードイン、自動再生動画、WebGLシーンには使用しません。

scroll-motion-graphics とは?

このスキル「scroll-motion-graphics」は、スクロール位置を唯一の時計として、ピクセル、キャプション、回転をすべてp ∈ [0,1]の純粋関数で描画する、高パフォーマンスなスクロール駆動アニメーション手法を提供します。 主な特徴は以下の通りです。 * **ビデオのようなスクラブ動作**: スクロールを前後に動かすだけで、アニメーションがフレーム単位で再生・巻き戻しされ、途中停止しても意味のあるフレームを表示します。 * **ページの乗っ取り防止**: ホイールやタッチ操作を妨害せず、通常のスクロール体験を維持します。 * **DOMベースのテキスト**: キャプションは常にDOM要素として存在するため、Canvas描画と分離され、常に可読性が保たれます。 * **高いパフォーマンス**: アイドル時はrequestAnimationFrameを停止し、CPU消費をゼロにします。また、prefers-reduced-motionにも対応しています。 対応プラットフォームは、Claude Code, Cursor, Codexなどの主要なAIエージェントです。このスキルは、独自のSKILL.mdに従い、純粋関数によるリール、CSSスティッキーによるピン留め、DOM内のテキスト管理、パフォーマンス最適化など、厳格なルールに基づいて実装します。複雑な「スクローリーテリング」やストーリードラムの構築に理想的で、提供されるデモファイル(assets/demo.html)から始めることで、簡単に開発を開始できます。

対応✓Claude Code~Codex CLI✓Cursor✓OpenCode
npx skills add https://github.com/shubham001312/imutechz-skills/tree/HEAD/skills/scroll-motion-graphics

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

スクロールモーショングラフィックス

スクロール位置によって駆動されるCanvasタイムライン:ピン留めされたステージ、進行度の純粋関数であるリール、そして描画が決して触れてはならないキャプション。この手法は、ImuTechzホームページのストーリードラム、R&Dドラム、Founderパスフィルムで使用されています。また、assets/demo.htmlの5リールデモでも採用されています。

このテクニック全体は一文で表現できます。スクロール位置が唯一の時計です。それをp ∈ [0, 1]にマッピングし、すべてのピクセル、キャプション、回転をpの関数にします。

理想の動作

  • ビデオのようにスクラブする。 前方にスクロールするとフィルムが再生され、後方にスクロールするとフレーム単位で巻き戻されます。途中で止めると、意味のあるフレームが表示されます。
  • ページが乗っ取られることはありません。 ホイール、トラックパッド、タッチ、キー操作は、他の場所とまったく同じようにページを動かします。セクションは単に高くなるだけです。
  • テキストは常に読みやすい。 キャプションはカード上の実際のDOMテキストです。描画はキャプションが残したスペースに自身を配置し、Canvasは安全策としてキャプションの周りでクリップされます。
  • 見せるためだけでなく、操作のためでもある。 ストーリードラムの面はリンクです。そのレール、矢印ボタン、矢印キー、マウスドラッグはすべてページをスクロールすることでシークするため、スクロールとドラムの間で矛盾が生じることはありません。
  • アイドル時のコストはゼロ。 pが安定した後、またはセクションがビューポートから外れた後は、requestAnimationFrameは実行されません。
  • 動きの軽減設定を尊重する。 スムージング、中間ドラム回転、キャプション出現アニメーションは行われません。すべてのコンテンツはそのまま存在します。
  • 360pxから1920pxの幅、ライトモードとダークモード、DPR 1と2で動作する。

厳格なルール

  1. pはスクロール位置から取得し、時計からは決して取得しない。 リール内で performance.now()、Date、CSSアニメーション、または setInterval を使用しないでください。スムージング(ターゲットpへのlerp)は、同じフレームに収束するため許可されます。
  2. リールは純粋関数 draw(ctx, t, env) である。 フレーム間で状態を保持せず、Math.random() を呼び出してはいけません。代わりに、シード付きの hash(i) を使用してください。同じ (t, env) は同じピクセルを描画しなければなりません。
  3. ピン留めはJSではなくCSSで行う。 高いトラック(position: relative; height: N×vh)を使用し、その中に position: sticky; top: 0; height: 100svh のステージを配置します。ステージに position: fixed を設定したり、bodyをロックしたり、トラックの祖先に overflow: hidden|auto|clip を設定したりしないでください。
  4. 入力は決して奪わない。 ホイールやタッチに対して preventDefault を使用せず、htmlやbodyに overflow: hidden を設定せず、トラックにスクロールスナップを設定しないでください。フォーカスされたドラムのみが矢印キーを処理でき、その場合もページをスクロールすることで行います。
  5. シークはスクロールで行う。 すべてのボタン、レールアイテム、キー、アンカーは window.scrollY を変更します。pや回転を直接設定しないでください。プログラムによるジャンプでアニメーションさせたくない場合は behavior: "instant" を使用してください。"auto" はページのCSS scroll-behavior: smooth に従うためです。
  6. テキストはDOM内に存在する。 キャプション、タイトル、リンクはHTMLです。Canvasテキストは、年号、軸の目盛り、ステップ名などの装飾ラベルに限定されます。
  7. 毎フレームキャプションを測定し、描画をキャプションから遠ざける。 各キャプションとHUD要素は立入禁止領域(data-keep)です。リールは env.free 内にレイアウトされ、エンジンは偶数奇数のクリップで立入禁止領域をクリップします。
  8. 一度に2つのキャプションを表示しない。 キャプションウィンドウは、各境界にギャップを設けてリール内に配置され、リールはそのギャップ内でクロスフェードします。
  9. アイドル時はループを停止する。 セクションがビューポートの近くにある場合にのみ、スクロールまたはリサイズ時にrAFを開始し(IntersectionObserver)、pがターゲットと等しくなったら停止します。
  10. Canvasのサイズは min(devicePixelRatio, 2) でステージに合わせる。 毎フレーム setTransform(dpr, …) を呼び出し、毎フレームクリアします。
  11. 色はCSSカスタムプロパティから取得する。 ライトモードのパレットをリールにハードコードしないでください。prefers-color-scheme の変更時にパレットを再読み込みします。
  12. prefers-reduced-motion: reduce を尊重する。 スムージングを使用せず、ドラムを面ごとにスナップさせ、キャプション出現アニメーションをオフにします。フレームは引き続きスクロールに追従します。これはユーザーが操作しているためです。

構築手順

この順序で行います。各ステップは次のステップの前にテスト可能です。

  1. まずコンテンツ。 チャプターを書きます。各チャプターのキッカー、タイトル、1~2文の説明、オプションのリンク。リールの数(3~7)を決め、それぞれに概ね80~100svhのスクロール範囲を与えます。
  2. トラックとステージ。 高いセクションとその中にスティッキーな100svhのステージを追加し、JSなしでピン留めされることを確認します(layout-and-captions 参照)。
  3. スクラブエンジン。 ScrollMotion.scrub または film(engine 参照)を配線し、pをログ出力します。ピン開始時に0、ピン終了時に1を読み取り、双方向に動くことを確認します。
  4. DOM内のキャプションとHUD。 ブレークポイントごとに配置し、data-keep としてマークし、エンジンにpから不透明度を制御させます。
  5. リールを一つずつ。 env.free 内で draw(ctx, t, env) を書き(drawing 参照)、次のリールを書く前に前後でスクラブします。
  6. モーション文法。 エントリー、ホールド、エグジットのエンベロープ、スターガー、ディウェル、クロスフェードを追加します(motion-grammar 参照)。
  7. インタラクティブ性(ドラムの場合)。 面のリンク、レール、前後ボタン、キー、ドラッグを追加します。これらはすべてスクロールでシークします。
  8. パフォーマンスとアクセシビリティのパス。 アイドルループ、DPR、動きの軽減、フォーカス順序、スクリーンリーダーテキスト、ダークモードをカバーします(performance-and-a11y 参照)。
  9. 検証。 ヘッドレススクリプトを使用して、360、390、414、768、1024、1280、1440、1920で検証します(verification 参照)。すべてのチェックが通るまでは完了と呼びません。

決定チェックリスト

Canvasフィルム or DOM/CSS変換?

  • 多数の小さな可動部品、線を描画するもの、パーティクル、チャート → Canvasフィルム(ScrollMotion.film)。
  • 少数のカード、画像、テキストパネル → scrub によって駆動されるDOM変換(ドラムはこちら)。
  • 本物の3Dメッシュやシェーダー → このスキルだけでは不十分です。WebGLを使用しますが、ルール1から5と9から12は守ってください。

ピンモード or ビューモード?

  • ストーリーが再生されている間、セクションが画面を保持する必要がある場合 → mode: "pin"(高いトラックとスティッキーステージ)。
  • マーキーやパララックスのように、効果がコンテンツとともに流れていく場合 → mode: "view"(ピン留めなし)。

トラックの高さは?

  • フィルムの場合は n × 80–100svh + 100svh、ドラムの場合は n × 70vh + 100vh を使用します。テストリーダーが「速すぎる」と言った場合は、高さを追加します。ホイールを奪って速度を落とすことは絶対にしないでください。

キャプションはどこに配置する?

  • デスクトップ(≥ 760 px):描画の横(左または右のカード)または広い下部カード。スマートフォン:常に下部シートで、描画はその上。
  • 固定サイトヘッダーがある場合は、HUDとドラムがそれを避けるように --sm-nav-h を設定します。

ドラムは必要か?

  • チャプターがユーザーが移動したいと思うもの(ページ、製品、セクション)である場合にドラムを使用します。それ以外の場合は、フィルムのみを使用します。

失敗モードと修正方法

症状原因修正
ステージがピン留めされずにスクロールアウトする祖先に overflow: hidden/auto/clip がある、またはステージに top がないoverflowを削除し(必要に応じてbodyに overflow-x: clip を使用)、top: 0 を設定する
アニメーションが前方にしか再生されない、またはスクロールアップ時にジャンプするフレーム間で状態が蓄積されている、またはリール内でクロックや Math.random を使用しているリールをtの純粋関数にする。ランダム性には hash(i) を使用する
描画がキャプションの下に表示されるリールがステージ全体にレイアウトされているenv.free 内にレイアウトする。キャプションに data-keep を保持し、エンジンにクリップさせる
フェード中にキャプションボックスが間違った場所で測定されるgetBoundingClientRect(変換を含む)を使用しているレイアウトボックスを測定する(offsetLeft/Top/Width/Height、boxIn 参照)
境界で2つのキャプションが重なるキャプションウィンドウが接触しているキャプションを 1 − cross でフェードアウトし、cross でフェードインする。リールはギャップ内でクロスフェードする
アイドルページでファンが回るrAFループが停止しないcur === target になったら停止する。IntersectionObserverでゲートする
スマートフォンでCanvasがぼやける / 4Kで遅いCanvasがCSSサイズである、またはDPRが制限されていないwidth = W × min(dpr, 2) と setTransform(dpr, 0, 0, dpr, 0, 0) を使用する
レールをクリックした後にドラムとページが一致しないコントロールが回転を直接設定したすべてのコントロールは scrollToProgress を呼び出す。回転は常にpからのみ取得される
ドラッグがガタつき、その後スナップバックするページに scroll-behavior: smooth があるため、scrollTo(x, y) がアニメーションするドラッグ中は scrollTo({ top, behavior: "instant" }) を使用する
ドラッグ後に面をクリックするとリンクが開くドラッグを終了するクリックが発火する4px以上動いたドラッグ後、1回のキャプチャフェーズクリックを飲み込む
URLバーが非表示になるときのモバイル100vhジャンプステージに vh を使用しているステージには 100svh を使用する。ステージ自身の高さでpを計算する
ダークモードでライトモードの描画が表示されるハードコードされた色readPalette で --sm-* トークンを読み取る。スキーム変更時に再読み込みする
ドラムの背面が透けて見えるbackface-visibility: hidden がない-webkit- プレフィックス付きで、すべての面に設定する
ドラム面のサムネイルが間違ったサイズで描画される3D変換された面の投影矩形を使用したclientWidth/clientHeight でサイズ指定する

ファイル

  • references/engine.md: p、スクラブループ、film()、シーク、Reactフック。
  • references/drawing.md: リールコントラクト、Canvas設定、ルート、ポップイン、パーティクル、パレット。
  • references/layout-and-captions.md: トラックとステージのCSS、立入禁止領域、自由矩形、クリップ、ドラムレイアウト。
  • references/motion-grammar.md: エンベロープ、スターガー、ディウェル、クロスフェード、ペーシング、イージング。
  • references/performance-and-a11y.md: アイドルループ、レイアウトコスト、動きの軽減、キーボード、フォーカス、スクリーンリーダー。
  • references/verification.md: 8つの幅、ヘッドレスBrave/Chromeスクリプト、手動パス。
  • assets/demo.html: スタンドアロン、依存関係ゼロ、ダブルクリックで実行。5つのリールとストーリードラム。
  • assets/scroll-motion.js: エンジン(scrub、film、drum、scrollToProgress とヘルパー)、UMD。
  • assets/reels.js: 5つのサンプルリール(orbit、path、stack、wave、close)とサムネイル用の still()。
  • assets/scroll-motion.css: フィルムとドラムのスタイル、ライトおよびダークトークン。
  • assets/ScrollFilm.jsx: フィルム用Reactラッパー、useScrub フック。
  • assets/StoryDrum.jsx: Reactストーリードラム、自動回転する DrumPreview。
  • assets/preview-orbit.jpg、assets/preview-path.jpg、assets/preview-close.jpg: 1440 × 900でのデモ静止画。

デモから始めてください。開いてソースを表示し、コンテンツを置き換えます。ファイル下部の配線は約60行です。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

関連スキル