建構捲動進度指標

網頁功能: 捲動驅動動畫

捲動進度指標是常見的使用者介面模式,可透過視覺方式呈現使用者在可捲動文件或容器中的進度。使用者捲動時,視覺元素會更新以反映其位置,清楚直觀地呈現已瀏覽和剩餘的內容。

導入方式

如要建立捲動進度指標,您需要:

  1. 做為進度列的元素。這個元素通常是 position: fixed 或 position: absolute,因此在使用者捲動時會持續顯示。
  2. 與捲動位置連結的動畫。

你可以透過以下方法減少警告:

  • 首先,建立 HTML 元素做為進度列。您可以依喜好設定這個元素的樣式。
  • 接著,在 CSS 中定義 @keyframes 動畫,縮放進度列。常見的做法是將元素從 scaleX(0) 縮放至 scaleX(1)。
  • 最後,將這個動畫套用至進度列元素,並將其 animation-timeline 設為捲動時間軸。這會指示瀏覽器根據最近祖系捲動器的捲動位置,驅動動畫進度。

範例程式碼

這段程式碼會使用 scroll() 函式建立的匿名捲動時間軸,在捲動時放大 #progress 元素。

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: scroll();
    }
  }
}

由於 scroll() 函式在 DOM 中的位置,它會追蹤 block 方向上最接近的上層捲動器,在這裡是根捲動器。

<body>
  <!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
  <div id="progress" aria-hidden="true"></div>
</body>

這段程式碼會使用 scroll-timeline 屬性建立的具名捲動時間軸,在捲動時放大 #progress 元素。

@media (prefers-reduced-motion: no-preference) {
  @supports ((animation-timeline: scroll())) {
    @keyframes grow-progress {
      from { transform: scaleX(0); }
      to { transform: scaleX(1); }
    }

    :root {
      scroll-timeline: --tl block;
    }

    #progress {
      position: fixed;
      left: 0; top: 0;
      width: 100%; height: 1em;
      background: red;

      transform-origin: 0 50%;
      animation: grow-progress auto linear;
      animation-timeline: --tl;
    }
  }
}

最佳做法

使用捲動驅動動畫時,請務必遵循幾項最佳做法,確保體驗流暢且無障礙:

  • 務必加入功能偵測:並非所有瀏覽器都支援捲動驅動動畫。使用 @supports (animation-timeline: scroll()) 檢查支援情形,並為不支援的瀏覽器提供備用方案。
    • 請勿使用 scroll-timeline-polyfill 套件做為備用策略,因為該套件的功能不完整,且有許多已知問題。
    • 如果動畫僅用於裝飾,請選擇漸進增強,且「不要」提供備用方案。
  • 務必從輔助技術閱讀流程中移除純裝飾元素:將 aria-hidden="true" 套用至純視覺捲動指標,確保螢幕閱讀器不會遇到空白、未命名的節點。
  • 務必尊重使用者偏好:部分使用者偏好網頁上較少動態效果。使用 prefers-reduced-motion 媒體查詢,為這些使用者停用或減少動畫。
  • 建議只為高效能 CSS 屬性製作動畫:如要讓動畫最流暢,請只為瀏覽器合成器執行緒可處理的屬性製作動畫,例如 transform 和 opacity。為 width 或 height 等其他屬性建立動畫,可能會導致效能問題。
  • 建議使用正確的宣告順序:使用 animation 簡寫屬性時,請在該屬性「之後」宣告 animation-timeline,以免簡寫重設時間軸。

使用 scroll() 函式建立捲動驅動動畫時:

  • 選用:明確指定捲動器。如果不是指定最接近的上層捲動器,請使用 scroll(root) 或 scroll(self) 明確指定要使用的捲動器。
    • 如果 root、nearest 或 self 不足,請使用命名的捲動時間軸。
  • 選用:明確指定要追蹤的軸:如果不是指定預設的 block 軸 (例如在水平捲軸中),請明確指定要追蹤的軸,使用 scroll(block) 或 scroll(inline)。

使用 scroll-timeline 屬性建立捲動驅動動畫時:

  • 建議使用 CSS <dashed-ident> 做為名稱。
  • 選用:明確指定要追蹤的軸。如果不是指定預設的 block 軸 (例如在水平捲動器中),請使用 scroll-timeline-axis 明確指定要追蹤的軸。
  • 務必確保查詢範圍有效:如果宣告 scroll-timeline 的元素不是動畫元素的平面樹狀結構祖系,請在共用祖系上使用 timeline-scope,提高 scroll-timeline 名稱的瀏覽權限。

備用策略

如果瀏覽器不支援捲動驅動動畫,可以使用備用方案重新建立視覺效果。備用方案通常是使用捲動事件監聽器 (用於 ScrollTimeline 效果) 或 IntersectionObserver API (用於 ViewTimeline 效果) 建構。

如果瀏覽器內建支援捲動式動畫,請一律使用原生 CSS 實作,因為這類實作的效能較佳。

請注意,並非所有效果都能使用備用方法重新建立。

針對這個用途,下列指令碼會為不支援捲動驅動動畫的瀏覽器套用備用方案。它會使用捲動事件監聽器追蹤根元素的捲動位置,並據此更新進度列的 transform 屬性。

<script>
  if (!CSS.supports('animation-timeline', 'scroll()')) {
    const progress = document.querySelector('#progress');

    window.addEventListener('scroll', () => {
      const scrollable = document.documentElement.scrollHeight - window.innerHeight;
      const scrolled = window.scrollY;
      const progressPercentage = (scrolled / scrollable);

      progress.style.transform = `scaleX(${progressPercentage})`;
    });
  }
</script>