Communitygithub.com

shubham001312/scroll-motion-graphics

用於建立或修復網頁上滾動驅動的動態圖形段落,例如:由滾動位置控制的畫布影片、固定的黏性「卷動敘事」舞台、與滾動連結的字幕章節,或是由滾動、拖曳及方向鍵驅動的 3D 故事轉盤或輪播。

scroll-motion-graphics 是什麼?

此技能提供了一套可擴展、基於最佳實踐的方法,用來建立以滾動位置為唯一時鐘的高效能卷動敘事動畫。其核心原則是將滾動位置映射到進度值 p (0 到 1),並讓所有的繪圖、字幕和旋轉角度都成為此 p 值的純函數。它支援 Canvas 影片(適合粒子、線條等複雜繪圖)以及 DOM/CSS 置換(適合卡片與文字面板,例如故事轉盤),並支援兩種模式:固定模式(舞台黏在視窗中)與瀏覽模式(效果隨內容移動)。此技能明確禁止劫持頁面滾動、使用計時器驅動動畫、或將文字繪製在 Canvas 上;相反地,它強制使用真實的 DOM 元素來顯示字幕,並用 CSS `position: sticky` 來實現固定效果,而非依賴 JavaScript。透過 IntersectionObserver 實現閒置時停止請求動畫影格 (rAF) 循環、限制裝置像素比率 (DPR) 上限為 2、並完全支援 `prefers-reduced-motion`。本文件提供的參考資料詳細說明了引擎、繪圖、版面、動作文法、效能與無障礙、以及驗證程序,並附有可直接執行的完整獨立示範檔案。

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

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

滾動動態圖形

一個由滾動位置驅動的畫布時間軸:一個固定的舞台、作為進度純函數的捲軸,以及繪圖永遠不允許碰觸的字幕。此方法驅動了 ImuTechz 首頁上的故事轉盤、研發轉盤以及創辦人路徑影片。它也驅動了 assets/demo.html 中的五捲示範。

整個技術可以用一句話概括。滾動位置是唯一的時鐘:將其映射到 p ∈ [0, 1],並讓每一個像素、字幕和旋轉都成為 p 的函數。

理想的成果

  • 它像影片一樣可來回拖曳。 向前滾動,影片播放。向後滾動,它逐格倒帶。停在半途,會顯示一個合理的畫面。
  • 頁面從未被劫持。 滾輪、觸控板、觸控和按鍵對頁面的操作方式與其他地方完全相同。段落只會變高。
  • 文字永遠清晰可讀。 字幕是真實的 DOM 文字,放在卡片上。繪圖佈局在字幕留下的空間中,而畫布會圍繞它們進行裁剪,作為安全網。
  • 它是一個控制項,而不僅僅是展示。 故事轉盤的各個面都是連結。其軌道、箭頭按鈕、方向鍵和滑鼠拖曳,都是透過滾動頁面來定位,因此滾動與轉盤永遠不會不一致。
  • 閒置頁面不耗費任何資源。 一旦 p 值穩定,或段落離開視窗,就不會執行任何 requestAnimationFrame。
  • 尊重減少動畫效果設定。 沒有平滑過渡、沒有轉盤中間滾動、也沒有字幕入場動畫。所有內容仍然存在。
  • 在 360 到 1920 像素寬的螢幕上都表現良好,支援淺色與深色模式,以及 DPR 1 和 2。

嚴格規則

  1. p 僅來自滾動位置,絕不來自時鐘。 請勿在捲軸內部使用 performance.now()、Date、CSS 動畫或 setInterval。允許平滑處理(向目標 p 值的線性插值 lerp),因為它最終會收斂到相同的畫面。
  2. 捲軸是純函數 draw(ctx, t, env)。它在影格之間不保留狀態,且絕不呼叫 Math.random();請改用種子化的 hash(i)。相同的 (t, env) 必須繪製出相同的像素。
  3. 使用 CSS 而非 JS 來固定。 使用一個高軌道(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。畫布文字僅限於裝飾性標籤,例如年份、軸刻度與步驟名稱。
  7. 每影格測量子幕位置,並讓繪圖避開它們。 每個字幕和 HUD 元素都是一個禁區(data-keep)。捲軸在 env.free 內部進行佈局,引擎則使用奇偶裁剪法則裁剪出這些禁區。
  8. 永遠不要同時顯示兩個字幕。 字幕視窗位於其捲軸內部,並在每個邊界處留有空隙,而捲軸則在該空隙內進行交叉淡入淡出。
  9. 閒置時停止迴圈。 僅在段落靠近視窗時(透過 IntersectionObserver),才在滾動或調整大小時啟動 rAF,並在 p 等於目標值時停止。
  10. 畫布的大小設定為舞台的 min(devicePixelRatio, 2) 倍。 每影格呼叫一次 setTransform(dpr, …),並在每影格清除畫布。
  11. 顏色來自 CSS 自訂屬性。 不要將淺色模式的調色板硬編碼到捲軸中。在 prefers-color-scheme 變更時重新讀取調色板。
  12. 尊重 prefers-reduced-motion: reduce。 不使用平滑效果,讓轉盤面對面直接跳轉,並關閉字幕入場動畫。畫面仍會跟隨滾動,因為滾動是由使用者驅動的。

建置順序

按此順序進行。每個步驟都可以在下一個步驟之前進行測試。

  1. 先有內容。 撰寫章節:每個章節包含一個導語、一個標題、一兩句話和一個可選的連結。選擇捲軸數量(3 到 7 個),並為每個捲軸分配大約 80 到 100 svh 的滾動空間。
  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)。直到所有檢查都通過,才能宣告完成。

決策檢查清單

畫布影片還是 DOM/CSS 轉換?

  • 許多小型移動部件、繪製的線條、粒子或圖表 → 畫布影片 (ScrollMotion.film)。
  • 少數卡片、圖片或文字面板 → 由 scrub 驅動的 DOM 轉換(故事轉盤是其中一種)。
  • 真正的 3D 網格或著色器 → 此技能不足夠。使用 WebGL,但保留規則 1 到 5 以及 9 到 12。

固定模式還是瀏覽模式?

  • 區段應在播放故事時保持在螢幕上 → mode: "pin"(高軌道和黏性舞台)。
  • 效果隨內容通過,例如跑馬燈或視差 → mode: "view",無需固定。

軌道要多高?

  • 影片使用 n × 80–100svh + 100svh,轉盤使用 n × 70vh + 100vh。如果測試人員說「太快」,則增加高度。永遠不要透過攔截滾輪來減慢速度。

字幕放哪?

  • 桌面版(≥ 760 像素):繪圖旁邊(左側或右側卡片)或作為一個寬的底部卡片。手機版:始終使用底部工作表,繪圖在其上方。
  • 如果有固定的網站頁首,設定 --sm-nav-h 以使 HUD 和轉盤避開它。

需要轉盤嗎?

  • 當章節是用戶可能想要前往的內容(頁面、產品、區段)時,使用轉盤。否則僅使用影片。

故障模式與修復

症狀原因修復
舞台滾走而非固定祖先元素有 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)
兩個字幕在邊界處重疊字幕視窗相接在 1 − cross 處淡出字幕,在 cross 處淡入;捲軸在空隙中交叉淡入淡出
風扇在閒置頁面上旋轉rAF 循環從未停止在 cur === target 時停止;透過 IntersectionObserver 控制
手機上畫布模糊 / 4K 上速度慢畫布為 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" })
拖曳後點擊某個面會打開其連結結束拖曳的點擊事件觸發在移動超過 4 像素的拖曳之後,吞掉一個捕獲階段的點擊事件
URL 列隱藏時 Mobile 100vh 跳動舞台使用了 vh舞台使用 100svh;用舞台自身的高度計算 p
深色模式顯示淺色模式繪圖硬編碼的顏色透過 readPalette 讀取 --sm-* token;在配色方案變更時重新讀取
轉盤的背面透出前景缺少 backface-visibility: hidden設定此屬性,並加上 -webkit- 前綴,套用於每個面
轉盤面縮圖繪製尺寸錯誤使用了 3D 變換面的投影矩形根據 clientWidth/clientHeight 設定大小

檔案

從示範開始:打開它,檢視原始碼並取代內容。檔案底部的接線程式碼約 60 行。

Individual skills in this repo

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

相關技能