Communitygithub.com

apple-scroll-animation

使用純 HTML/CSS/JS 建立 Apple 風格的滾動驅動動畫(scrollytelling),無需任何程式庫。當使用者想要頁面像 apple.com 產品頁一樣,隨著滾動而組裝、發光、傾斜、展開或切換時使用。涵蓋固定(黏性)區段、滾動進度擦洗、逐字顯現、元素飛入以及無頭測試方法。

apple-scroll-animation 是什麼?

這項技能提供了一個完整的框架,用於建立 Apple 風格的「滾動說故事」(scrollytelling)效果,完全依賴純 HTML、CSS 和 JavaScript,無需任何第三方程式庫。其核心概念是將所有動畫效果與滾動位置同步,而非時間觸發,這意味著使用者向上滾動時動畫會反向播放。技能文件詳細說明了三個核心概念:固定區段(使用 sticky 定位和額外高度作為動畫預算)、進度計算函數,以及根據進度驅動視覺效果(如位移、旋轉、縮放、透明度等)。此外,它還列舉了多種實作效果,例如組裝、逐字發光、固定步驟器、傾斜裝置模型、卡片浮入和展開效果,並提供了實用的規則以避免常見陷阱(例如使用個別 CSS 屬性而非 transform 以避免與滑鼠懸停衝突)。支援的平台包括任何標準網頁瀏覽器,並且包含了透過 Chrome DevTools Protocol 進行無頭測試的指南。總體而言,這項技能讓開發者能夠創造出流暢、互動且視覺豐富的滾動敘事頁面。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/MananShukla007/apple-scroll-animation/tree/HEAD/skills/apple-scroll-animation

在你喜歡的 AI 中提問

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

說明文件

Apple 風格滾動動畫

效果是由滾動位置擦洗,而非由計時器觸發。向上滾動時會反向播放。無需任何程式庫。

三個核心概念

  1. 固定(Pin)。一個高的外層容器(例如 height: 300vh)包含一個 position: sticky; top: 0; height: 100vh; overflow: hidden 的子元素。該子元素會停留在畫面上,而外層容器則繼續滾動。外層容器的額外高度就是您的動畫預算。
  2. 進度。一個函數將滾動位置轉換為 0..1 之間的數字:
    • 固定區段:clamp(-rect.top / (rect.height - innerHeight))
    • 元素進入視口:clamp((innerHeight*a - rect.top) / (innerHeight*(a-b)))
  3. 根據數字驅動視覺效果。每幀設定 translate、rotate、scale、opacity(和 borderRadius)。一個使用 requestAnimationFrame 節流的 scroll 監聽器會重新計算所有內容。

可直接套用的引擎:references/scroll-engine.js。

效果良好的動畫(選擇 3-5 個,不要全用)

效果做法
組裝項目從視口邊緣各處散落(隨機角度、旋轉、縮放 .35、透明度 .2)並漸變到其佈局位置。載入/調整大小時計算每個項目到其自然位置的偏移量,然後乘以 (1 - ease(p))。按索引交錯。
逐字發光將每個單字包裹在 span 中。透明度 .18 → 1 作為 clamp(p * 1.25 * N - k)。
固定步驟器step = floor(p * N);分數 p*N - step 填充活動步驟內的進度條。點擊步驟應 scrollTo 該步驟的位置。
傾斜裝置模型對於手機/裝置模型:perspective(1400px) rotateX((1-e)*22deg) translateY((1-e)*60px) scale(.84 + .16*e)。
卡片浮入translate: 0 (1-e)*80px; scale: .9+.1e; opacity: e,按欄位延遲(i % cols)。
展開影片/圖片 scale .78 → 1 以及 borderRadius 44px → 28px。

在應用進度之前,使用 ease = t => 1 - (1-t)**3。

規則(從經驗中學到)

  • 使用個別的 CSS 屬性 translate、rotate、scale 進行擦洗,並將 transform 保留給滑鼠懸停。否則滑鼠懸停和滾動會爭奪同一個屬性。
  • 不要在同一個元素上同時使用 .reveal 淡入類別和擦洗的 transform。選擇其中一個。
  • 將區段標題放在固定面板內部與內容一起。如果面板只包含內容並且居中顯示,高螢幕會出現大片空白區域。
  • 保持滾動距離適中。對於 N 步驟的步驟器,約為 N * 55vh + 100vh;對於一次性效果,約為 240-300vh。使用者會覺得 700vh 以上很累人。
  • 限制固定區塊的內容以適應 100vh。在短螢幕(max-height: 820px)和手機上,隱藏副標題和其他額外內容,並在步驟器中僅顯示當前活動步驟。
  • 減少動畫:如果 prefers-reduced-motion: reduce,則在所有地方回傳進度 1(最終狀態,無移動)。不要讓內容保持隱藏。
  • Sticky 定位的上層元素不需要 overflow,除了 body 上的 overflow-x: hidden(這是可以的)。避免在 sticky 元素和頁面之間的任何包裝器上使用 overflow: hidden。
  • 在調整視窗大小時重新計算(組裝偏移量依賴於佈局)。
  • 僅對 translate/rotate/scale/opacity/border-radius 進行動畫處理;避免使用會觸發佈局的屬性。
  • 避免影片的開頭或結尾片段過短:如果擦洗 <video> 播放,短片段可能顯示為空白。

典型頁面順序

英雄區(固定、組裝)→ 一個簡單陳述(無效果)→ 證明/影片(展開)→ 固定步驟器 → 裝置模型(傾斜裝置模型)→ 卡片網格(浮入)→ 行動呼籲。交替使用黑色和淺灰色區段以營造 Apple 風格;一個強調色。

測試

無頭 Chrome 的 --screenshot 無法滾動。請改為透過 DevTools 協議驅動真實的 Chrome:

  1. chrome --headless=new --remote-debugging-port=9333 --user-data-dir=/tmp/prof about:blank &
  2. 從 Node(v22+ 有全域 WebSocket):取得 http://localhost:9333/json,開啟頁面的 webSocketDebuggerUrl,發送 Emulation.setDeviceMetricsOverride、Page.navigate,然後對於每個位置發送 Runtime.evaluate("scrollTo(0,Y)"),等待約 700 毫秒,再發送 Page.captureScreenshot。
  3. 從區段的 offsetTop 和高度選擇 Y(例如每個固定區段的 0%、60%、100%)。
  4. 在桌面(1440x900)、大螢幕(2000x1300)和手機(390x800)上檢查,並確認 document.documentElement.scrollWidth === innerWidth(無水平溢出)。

交付清單

  • 每個效果都由滾動進度驅動,可反向播放
  • 減少動畫的備用方案
  • 已檢查手機佈局,無水平滾動
  • 固定區塊的滾動長度適中;高螢幕上無空白區域
  • 在每個固定區塊的開始/中間/結束處拍攝螢幕截圖

相關技能