輪轉投影片效果可為輪轉介面增添視覺趣味。使用者捲動投影片時,每張投影片在進入、置中和離開捲動區時,都能播放動畫。例如,投影片可以淡入和淡出、旋轉或縮放大小。打造生動有趣的互動體驗。與簡單的進入/退出動畫不同,這項效果會使用單一連續動畫,控制投影片在整個捲動區塊中的外觀。
導入方式
建立輪轉投影片效果的方法如下:
建立捲動器:這個元素會做為輪轉介面投影片的容器。在本範例中,它使用
overflow-x: scroll允許水平捲動。<ul class="scroller"> <li class="entry">1</li> <li class="entry">2</li> <li class="entry">3</li> … </ul>.scroller { overflow-x: scroll; }定義動畫:建立 CSS 動畫,定義投影片在捲動區中移動時的不同狀態。您可以為動畫的任何部分定義主要畫面格。舉例來說,您可以加入
50%關鍵影格,定義投影片位於捲動區塊中央時的狀態。在這個範例中,scale屬性會使投影片在接近中心時放大,在遠離中心時縮小。@keyframes animate { 0% { scale: 0.5; } 50% { scale: 1; } 100% { scale: 0.5; } }套用動畫和
view-timeline:將動畫附加至輪轉介面投影片,並連結至view-timeline,以便在元素捲動容器時追蹤元素。.scroller > * { animation: animate auto linear both; animation-timeline: view(inline); }根據預設,
view()會追蹤block軸上的元素。如需在inline軸上追蹤,可以使用view(inline)。
範例程式碼
這段程式碼會使用匿名檢視畫面時間軸,在捲動時為水平捲動器的輪轉介面項目建立動畫:
@keyframes animate {
0% {
scale: 0.5;
}
50% {
scale: 1;
}
100% {
scale: 0.5;
}
}
.scroller > * {
/* Applies the animation using an `auto` duration */
animation: animate auto linear both;
/* Sets the animation timeline to use an anonymous view progress timeline, tracking the element's progress through the scroller on the inline axis */
animation-timeline: view(inline);
}
這段程式碼會使用具名檢視畫面時間軸,在捲動時為水平捲動器的輪轉介面項目建立動畫:
@keyframes animate {
0% {
scale: 0.5;
}
50% {
scale: 1;
}
100% {
scale: 0.5;
}
}
/* This creates a named view-timeline on each carousel item. The timeline is used to drive the animation that is applied on the same element. */
.scroller > * {
/* Applies the animation using an `auto` duration */
animation: animate auto linear both;
/* Defines a named view progress timeline, tracking the element's progress through the scroller on the inline axis */
view-timeline: --item inline;
/* Sets the animation timeline to use the named view progress timeline defined above */
animation-timeline: --item;
}
最佳做法
使用捲動驅動動畫時,請務必遵循幾項最佳做法,確保體驗流暢且無障礙:
- 務必加入功能偵測:並非所有瀏覽器都支援捲動驅動動畫。使用
@supports ((animation-timeline: view()) and (animation-range: entry))檢查支援情形,並為不支援的瀏覽器提供備用方案。- 這裡必須加入
(animation-range: entry)檢查,篩除僅部分支援的瀏覽器。 - 請勿使用
scroll-timeline-polyfill套件做為備用策略,因為該套件的功能不完整,且有許多已知問題。 - 如果動畫僅用於裝飾,請選擇漸進增強,且「不要」提供備用方案。
- 這裡必須加入
- 務必尊重使用者偏好:部分使用者偏好網頁上較少動態效果。使用
prefers-reduced-motion媒體查詢,為這些使用者停用或減少動畫。 - 建議只為高效能 CSS 屬性製作動畫:如要讓動畫最流暢,請只為瀏覽器合成器執行緒可處理的屬性製作動畫,例如
transform和opacity。為width或height等其他屬性建立動畫,可能會導致效能問題。 - 建議使用正確的宣告順序:使用
animation簡寫屬性時,請在之後宣告animation-timeline和animation-range,以免簡寫重設時間軸。
如果多個 DOM 元素需要根據相同時間軸建立動畫,或您需要為已定義 view-timeline 的元素子項建立動畫,建議使用已命名的 view-timeline。如果動畫元素也是定義 view-timeline 的元素,您可以使用 view() 建立匿名檢視時間軸。
使用 view() 函式建立捲動驅動動畫時:
- 選用:明確指定要追蹤的軸:如果不是指定預設的
block軸 (例如在水平捲軸中),請明確指定要追蹤的軸,使用view(block)或view(inline)。
使用 view-timeline 屬性建立捲動驅動動畫時:
- 務必使用 CSS
<dashed-ident>做為名稱 (例如view-timeline: --my-custom-name) - 選用:明確指定要追蹤的軸。如果不是指定預設的
block軸 (例如在水平捲動器中),請使用view-timeline-axis明確指定要追蹤的軸。 - 務必確保查詢範圍有效:如果宣告
view-timeline的元素不是動畫元素的平面樹狀結構祖系,請在共用祖系上使用timeline-scope,提高view-timeline名稱的瀏覽權限。
瀏覽器支援和備用策略
因此,通常需要備用策略。
如果瀏覽器不支援捲動驅動動畫,可以使用備用方案重新建立視覺效果。備用方案通常是使用捲動事件監聽器 (用於 ScrollTimeline 效果) 或 IntersectionObserver API (用於 ViewTimeline 效果) 建構。
如果瀏覽器內建支援捲動式動畫,請一律使用原生 CSS 實作,因為這類實作的效能較佳。
請注意,並非所有效果都能使用備用方法重新建立。
針對這個用途,下列指令碼會為不支援捲動驅動動畫的瀏覽器套用備用方案。這個函式會使用 Web Animations API (Element.animate()),為輪轉介面中的每個項目建立暫停的動畫。接著,它會監聽捲動器上的 scroll 事件,並根據項目在捲動器中的捲動進度,更新每個動畫的 currentTime。
// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: view()) and (animation-range: entry)')) {
const scroller = document.querySelector('.scroller');
const entries = document.querySelectorAll('.entry');
// Create a map to store animations
const animations = new Map();
entries.forEach(entry => {
const animation = entry.animate(
{
scale: ['0.5', '1', '0.5']
},
{
duration: 1, // We'll control the time ourselves
fill: 'both'
}
);
animation.pause();
animations.set(entry, animation);
});
// Update animations on scroll
const tick = () => {
const scrollerRect = scroller.getBoundingClientRect();
entries.forEach(entry => {
const animation = animations.get(entry);
if (!animation) return;
const entryRect = entry.getBoundingClientRect();
const progress = (entryRect.left + entryRect.width / 2 - scrollerRect.left) / scrollerRect.width;
animation.currentTime = progress;
});
};
scroller.addEventListener('scroll', tick);
tick();
}