Scroll progress animations in CSS. Learn how to link animations to the scroll progress of a container. A vibrant gradient behind artwork of computer graphic with code and a window with a scrollbar.

CSS 中的滾動進度動畫

閱讀時間 7 分鐘

滾動連結動畫通常能為網站增添一抹亮色,但長期以來一直被 JavaScript 所主導。現在,一項全新的規範正在實施,使我們能夠使用 CSS 建立豐富的滾動驅動體驗!

當我們想到滾動驅動動畫時,我們通常會想到兩種情況:

  • 動畫在使用者滾動時發生,動畫的進度與滾動進度明確關聯。例如,長文章的進度條。
  • 當元素進入、退出或在可視區域內移動時發生的動畫——通常是視口,但也可能是另一個可滾動容器的可見部分(這被定義為滾動視口)。

滾動驅動動畫規範》涵蓋了這兩種型別的動畫。在本文中,我們將首先看看滾動進度時間線,顧名思義,它將動畫與滾動進度關聯起來。

注意: 在撰寫本文時,此帖中的功能在瀏覽器支援方面有限。最好使用 Chrome Canary,但您也可以在 Chrome 115 或更高版本中啟用實驗性功能來跟隨示例,並自己嘗試滾動連結動畫。

使用動畫時間線

在這個例子中,我們將實現一個常見功能:當用戶滾動網頁時,一個簡單的進度條會從左到右縮放。因為我們希望將動畫與根滾動器的進度關聯起來,所以我們可以使用一個匿名的滾動進度時間線。

首先,讓我們定義動畫本身。我們希望進度條從左到右縮放,所以我們將使用transform

css
@keyframes scaleProgress {
  0% {
    transform: scaleX(0);
  }
  100% {
    transform: scaleX(1);
  }
}

為了將我們的進度條元素的動畫與滾動進度關聯起來,我們使用了animation-timeline 屬性,並將scroll() 函式作為其值。

css
.progress {
  animation-timeline: scroll();
}

scroll() 函式允許我們指定滾動容器和軸。預設值是 scroll(nearest block),這意味著動畫將與塊軸上最近的可滾動祖先元素關聯。這足以滿足我們的目的,儘管我們可以選擇性地將根元素指定為滾動容器,因為我們希望明確地將動畫與視口的滾動進度關聯起來。

css
.progress {
  animation-timeline: scroll(root block);
}

最後,我們需要將動畫新增到進度條元素上,並將我們的關鍵幀動畫作為animation-name。我們需要將動畫持續時間設定為 auto,因為持續時間將由滾動進度決定。我們還將緩動(animation-timing-function)設定為 linear,以便它與滾動同步平滑進行。如果我們使用預設值(ease),動畫將開始緩慢,然後迅速加速,最後在結束時減速——這與我們想要的進度指示器不符!

css
.progress {
  animation-timeline: scroll(root);
  animation-name: scaleProgress;
  animation-duration: auto;
  animation-timing-function: linear;
}

我們可以使用 animation 簡寫屬性來稍微簡化這一點。

css
.progress {
  animation: scaleProgress auto linear;
  animation-timeline: scroll(root);
}

注意: animation-timeline 目前不包含在簡寫屬性中。但是,animation 屬性會將 animation-timeline 重置為 auto(預設值),因此我們需要在 animation 簡寫屬性之後宣告 animation-timeline

多個動畫

就像常規的關鍵幀動畫一樣,我們可以同時應用多個滾動時間線動畫,例如改變進度條的顏色。

css
.progress {
  animation:
    scaleProgress auto linear,
    colorChange auto linear;
  animation-timeline: scroll(root);
}

@keyframes scaleProgress {
  0% {
    transform: scaleX(0);
  }
  100% {
    transform: scaleX(1);
  }
}

@keyframes colorChange {
  0% {
    background-color: red;
  }
  50% {
    background-color: yellow;
  }
  100% {
    background-color: lime;
  }
}

使用不同的緩動函式

雖然我們在前面的例子中特意選擇了線性緩動,但我們也可以使用 steps() 緩動來實現一些有趣的效果。這個例子展示了一種不同型別的進度條,它採用離散步長而不是平滑的線性縮放。我們為進度條元素設定了線性漸變背景作為顏色段,然後透過動畫化 clip-path 來逐個顯示它們。

css
.progress {
  background: linear-gradient(
    to right,
    red 20%,
    orange 0,
    orange 40%,
    yellow 0,
    yellow 60%,
    lime 0,
    lime 80%,
    green 0
  );
  animation: clip auto steps(5) forwards;
  animation-timeline: scroll(root);
}

@keyframes clip {
  0% {
    clip-path: polygon(0 0, 0 0, 0 100%, 0 100%);
  }
  100% {
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  }
}

重複和反向播放動畫

滾動進度動畫可以與現有的 animation-directionanimation-iteration-count 屬性結合使用。因此,我們可以讓我們的動畫在滾動時間線中重複多次,或者反向播放。這裡,“球”在我們滾動時會彈跳幾次。

css
.progress {
  animation: bounce auto linear 6 alternate;
  animation-timeline: scroll(root);
}

@keyframes bounce {
  100% {
    transform: translateY(-50vh);
  }
}

定位非祖先滾動容器

有時,我們可能希望為是滾動容器後代的元素設定動畫,但仍將其動畫與滾動容器的進度關聯起來。為了做到這一點,我們需要建立一個命名滾動進度時間線。我們將透過使用簡寫屬性 scroll-timelinescroll-timeline-namescroll-timeline-axis 的簡寫)在滾動容器上宣告時間線的名稱和軸。同樣,塊軸是預設值。時間線名稱必須帶有兩個破折號字首(類似於自定義屬性),這可以確保它不會與其他屬性值衝突。

滾動容器必須是一個具有滾動能力的元素。

css
.scroller {
  max-height: 300px;
  overflow: scroll;
  scroll-timeline: --scale-progress block;
}

我們可以使用 animation-timeline 屬性將我們要設定動畫的元素與滾動時間線關聯起來。

css
/* Sibling of .scroller */
.progress {
  animation: scaleProgress auto linear;
  animation-timeline: --scale-progress;
}

@keyframes scaleProgress {
  0% {
    transform: scaleX(0);
  }
  100% {
    transform: scaleX(1);
  }
}

為滾動容器的祖先元素設定動畫

只要我們要設定動畫的元素是滾動容器的同級元素,這就可以正常工作。如果我們想為祖先元素或同級元素的後代元素設定動畫怎麼辦?

我們還需要另一個 CSS 屬性,timeline-scope,它允許我們修改命名時間線的範圍,以包含設定該屬性的元素。例如,如果我們將其設定在 body 上,我們就可以為該元素的背景顏色設定動畫,儘管它是一個滾動容器的祖先。

Image

讓我們看一下程式碼。

css
/* Ancestor element: We want to scope the scroll timeline to include this element and its descendants */
body {
  timeline-scope: --scale-progress;

  /* Apply the animation */
  animation: colorChange auto linear forwards;
  animation-timeline: --scale-progress;
}

/* The scroll container on which we declare our timeline */
.scroller {
  max-height: 300px;
  overflow: scroll;
  scroll-timeline: --scale-progress block;
}

/* Apply the animation on the sibling as before */
.progress {
  animation: scaleProgress auto linear;
  animation-timeline: --scale-progress;
}

注意: timeline-scope 目前僅在 Chrome Canary 和 Chrome 116 中支援,且需要啟用實驗性 Web 平臺功能。

探索創意示例

到目前為止,我們已經建立了一些相當基本的進度條動畫——這可能是滾動進度時間線最明顯的用例之一。但是,沒有任何東西能阻止我們用滾動動畫發揮創意。

水平圖片滾動器

在使用者垂直滾動時水平動畫化元素可以使網頁感覺更具動態性,不那麼線性。這裡,我們動畫化一系列圖片,讓它們在使用者垂直滾動時從左側滑入。

在 CodePen 上檢視完整示例。

使用運動路徑

我們可以使用 offset-path 在 CSS 中定位和動畫化元素沿路徑移動,以定義元素要遵循的運動路徑。這比矩形進度條有趣得多!

在 CodePen 上檢視完整示例。

組合多個動畫

在此演示中,我們在滾動時為多個元素設定動畫:文字被顯示出來,而盒子從左到右滑動並翻轉。為了簡化程式碼並避免建立多個關鍵幀,我們動畫化了一個自定義屬性,並使用三角函式來計算 translateY 值,這些函式在所有主要瀏覽器的最新版本中都受支援。與 transform 屬性不同,自定義屬性是在主執行緒上進行動畫處理的,這意味著如果您試圖為它們中的許多設定動畫,您的網站可能會出現效能不佳的情況。

在 CodePen 上檢視完整示例。

可訪問性和使用者運動偏好設定

與任何侵入性動畫一樣,我們應該始終優先考慮可訪問性,並確保為那些寧願不使用動畫的使用者關閉動畫。這對於滾動驅動動畫尤其重要,因為即使是那些通常不患有前庭疾病的使用者,也可能引起暈動症。如果您想了解更多資訊,請參閱尊重使用者的運動偏好,瞭解如何使用 prefers-reduced-motion 媒體查詢來確保您的動畫是可訪問的。

總結

那麼,CSS 中的滾動時間線動畫與 JavaScript 庫(一旦它們得到普遍支援)相比如何?如果您正在建立特別複雜的動畫,您可能仍然需要使用像 GSAP 這樣的庫,它特別擅長處理複雜的編排。庫也可能為我們提供自定義緩動等功能,以及 GSAP 的 Inertia 外掛(允許動畫在滾動停止後滑動停止,而不是 abrupt 停止)。目前,我們還沒有在 CSS 中檢測元素當前是否正在滾動的辦法。

同樣,如果您的動畫對使用者體驗至關重要,您可能需要暫時等待,因為滾動連結動畫普遍得到支援還需要一段時間。

另一方面,如果您需要一些相對簡單的滾動驅動動畫,CSS 可以為您(和您的使用者)節省大量 JavaScript 負載,從而在效能上獲得巨大提升!

希望您喜歡閱讀本文並探索示例。如果您有任何反饋、想法或問題,請隨時在 DiscordGitHub 上留言。

有用資源