無論喜歡還是討厭,癱瘓相關內容將繼續保持。只要使用得當,就能為網頁應用程式增添深度和細緻感。不過,以高效能方式實作平移效果可能會相當困難。本文將討論一項兼具效能和跨瀏覽器相容性的解決方案。
重點摘要
- 請勿使用捲動事件或
background-position
建立視差動畫。 - 使用 CSS 3D 轉換功能,打造更精確的視差效果。
- 如果是行動版 Safari,請使用
position: sticky
,確保能夠傳播視差效果。
如要使用即插即用解決方案,請前往 UI Element 範例 GitHub 存放區,並取得 Parallax 輔助 JS!您可以在 GitHub 存放區中查看視差捲動器的即時示範。
Parallaxer 問題
首先,我們來看看兩種常見的視差效果實作方式,並特別說明為何這些方式不適合用於我們的用途。
不當做法:使用捲動事件
視差效果的主要要求是必須與捲動連結,也就是說,每當網頁捲動位置有任何變更,視差效果元素的位置都應更新。雖然這聽起來很簡單,但新式瀏覽器的重要機制是其異步運作的能力。這適用於捲動事件,在特定情況下。在大多數瀏覽器中,捲動事件會以「盡力」方式傳送,且無法保證會在捲動動畫的每個影格中傳送!
下列重要資訊可讓我們瞭解為何必須避免使用基於 JavaScript 的解決方案,以根據捲動事件來移動元素: JavaScript 不保證視差會隨著頁面的捲動位置而改變。在舊版 Mobile Safari 中,捲動事件實際上是在捲動結束時傳送,因此無法製作以 JavaScript 為基礎的捲動效果。較新版本「會」在動畫播放期間傳送捲動事件,但「盡力」仍會與 Chrome 類似,這點與 Chrome 類似。如果主執行緒忙於處理其他工作,捲動事件就不會立即傳送,這表示視差效果會消失。
錯誤:正在更新 background-position
我們也建議您避免在每個影格上繪製。許多解決方案都會嘗試變更 background-position
,以提供視差樣式,讓瀏覽器在捲動時重新繪製頁面上受影響的部分,進而導致動畫卡頓。
如要實現視差動畫效果,我們需要可做為加速屬性 (目前指的是轉換和不透明度) 的元素,且不依賴捲動事件。
3D 中的 CSS
Scott Kellum 和 Keith Clark 都曾在使用 CSS 3D 實現視差動畫方面做出重大貢獻,而他們使用的技巧如下:
- 設定內含
overflow-y: scroll
(也可能是overflow-x: hidden
) 捲動的包含元素。 - 將
perspective
值套用至相同元素,並將perspective-origin
設為top left
或0 0
。 - 針對該元素的子項,在 Z 軸上套用平移,並將其縮放回原大小,以提供視差動畫,而不影響其在螢幕上的大小。
這個方法的 CSS 如下所示:
.container {
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
perspective: 1px;
perspective-origin: 0 0;
}
.parallax-child {
transform-origin: 0 0;
transform: translateZ(-2px) scale(3);
}
假設有一個類似下方的 HTML 程式碼片段:
<div class="container">
<div class="parallax-child"></div>
</div>
調整透視縮放比例
將子項元素推回,會使該元素比例減少與視角值。您可以使用這個方程式計算需要擴大的幅度:(perspective -是從) / spec。由於我們很可能希望視差元素以視差效果顯示,但以我們建立的大小顯示,因此需要以這種方式放大,而非維持原樣。
在上述程式碼的情況下,透視效果為 1px,parallax-child
的 Z 距離為 -2px。這表示元素需要向上縮放 3 倍,您可以看到程式碼中插入的值:scale(3)
。
對於未套用 translateZ
值的任何內容,您可以改用零值。也就是說,比例為 (perspective - 0) / perspective,淨值為 1,表示比例既未縮小也未放大。真的很方便。
這個方法的運作方式
我們很快就會說明這項知識,請務必釐清這個做法的成因。捲動畫面實際上是一種轉換,因此可以加速捲動畫面;這類轉換通常會涉及使用 GPU 移動圖層。在典型的捲動中 (沒有任何透視概念),比較捲動元素及其子項時,捲動會以 1:1 的方式進行。如果您將元素向下捲動 300px
,則其子項會向上轉換相同的數量:300px
。
不過,將透視值套用至捲動元素會影響這個程序,因為會變更支撐捲動轉換的矩陣。現在,視您選擇的 perspective
和 translateZ
值而定,300 像素的捲動只能移動 150 像素。如果元素的 translateZ
值為 0,則會以 1:1 的速度捲動 (與以往相同),但在 Z 軸上推送的子項如果離開透視原點,則會以不同的速度捲動!淨結果:視差動態。而且重要的是,這會做為瀏覽器內部捲動機器的自動處理,因此您不需要監聽 scroll
事件或變更 background-position
。
行動版 Safari 的缺點
每個效果都有相關的警告,其中一個重要的警告是,轉換效果會保留子元素的 3D 效果。如果元素間的階層結構中,有視透視子項和視差子項之間的元素,3D 視角就會「扁平化」,表示效果遺失。
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
在上述 HTML 中,.parallax-container
是新的,它會有效地將 perspective
值平坦化,並讓我們失去視差效果。在大多數情況下,解決方案相當簡單:您只要將 transform-style: preserve-3d
新增至元素,即可讓元素傳播任何已在樹狀結構中更高層級套用的 3D 效果 (例如透視值)。
.parallax-container {
transform-style: preserve-3d;
}
不過,在行動版 Safari 的情況下,情況就比較複雜。從技術層面來說,將 overflow-y: scroll
套用至容器元素是可行的,但這樣做會導致無法快速捲動捲動元素。解決方案是新增 -webkit-overflow-scrolling: touch
,但這也會使 perspective
變平,我們不會看到任何視差。
從漸進式強化觀點來看,這個問題可能不是太多的問題。如果我們無法在所有情況下使用視差效果,應用程式仍可正常運作,但我們很樂意找出解決方法。
position: sticky
救星出動!
事實上,position: sticky
的形式可提供一些協助,讓元素在捲動期間「固定」在檢視區頂端或特定父項元素上。這項規格與大多數規格一樣相當龐大,但其中包含一個實用的寶貴資源:
乍看之下,這可能看似是個划算的交易,但這個語句的重點在於指出元素的固定度,確切來說是「偏移值的計算方式:透過捲動方塊參照最接近的祖系」。換句話說,系統會在套用任何其他轉換的前,而不是之後,計算移動固定元素的距離 (以便讓該元素顯示為附加至其他元素或 viewport)。也就是說,就像先前的捲動範例一樣,如果偏移量計算結果為 300 像素,則在將 300 像素偏移量值套用至任何固定元素之前,您可以使用觀點 (或任何其他轉換) 來操控該值。
將 position: -webkit-sticky
套用至視差元素,即可有效「反轉」-webkit-overflow-scrolling:
touch
的平坦效果。這可確保視差元素透過捲動方塊 (在本例中為 .container
) 參照最近的祖系。接著,與先前相同,.parallax-container
會套用 perspective
值,藉此變更計算的捲動偏移量,並產生視差效果。
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
.container {
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
.parallax-container {
perspective: 1px;
}
.parallax-child {
position: -webkit-sticky;
top: 0px;
transform: translate(-2px) scale(3);
}
這個動作會還原行動版 Safari 的視差效果,這真是好消息!
固定式定位的注意事項
不過,這裡有差異:position: sticky
會變更視差效果機制。固定位置會嘗試將元素固定在捲動容器中,而非固定版本則不會。也就是說,具有固定端的視差效果會與不具固定端的視差效果相反:
- 使用
position: sticky
時,元素越接近 z=0,移動的距離就會越少。 - 如果「不使用」
position: sticky
,則較靠近元素會移至 z=0,則會移動「更多」。
如果看起來較為抽象,請觀看 Robert Flack 提供的示範影片,瞭解各元素在固定位置和未固定位置時的行為有何差異。如要查看差異,您需要使用 Chrome Canary (撰寫本文時的版本為 56) 或 Safari。
Robert Flack 的示範:顯示 position: sticky
如何影響視差捲動。
各種錯誤和解決方法
不過,如同任何事情一樣,仍有需要處理的突起和凹陷:
- 固定功能不一致。Chrome 仍在實作支援功能,Edge 完全不支援,而 Firefox 在將固定元素與透視轉換結合時會發生繪圖錯誤。在這種情況下,建議您加入一些程式碼,以便在需要時只新增
position: sticky
(-webkit-
前置字元版本),這僅適用於 Mobile Safari。 - 這不只在 Edge 中「有效」而已。Edge 會嘗試在作業系統層級處理捲動作業,這通常是件好事,但在這種情況下,這會導致 Edge 無法在捲動期間偵測透視圖變化。如要修正這個問題,您可以新增固定位置元素,因為這項操作似乎會將 Edge 切換至 非 OS 捲動方法,並確保其因應視角變更。
- 「網頁上的內容數量龐大!」許多瀏覽器在決定網頁內容大小時會考量到縮放比例,但很遺憾,Chrome 和 Safari 不會考量到視角。因此,如果有 (例如,元素套用 3 倍的比例值) 時,即使該元素在套用
perspective
後大小為 1 倍,您還是會看到捲軸和其他類似效果。您可以透過從右下角縮放元素 (使用transform-origin: bottom right
) 來解決這個問題,因為這會導致過大元素擴展至捲動區域的「負面區域」(通常是左上方);捲動區域永遠不會讓您查看或捲動至負面區域中的內容。
結論
請謹慎使用視差效果,是有趣的效果。如您所見,可以以效能高、與捲動畫面相關聯,以及跨瀏覽器的方式實作。由於這項操作需要一點數學技巧,以及少量程式碼模板才能獲得所需效果,因此我們已包裝一個小型輔助程式庫和範例,您可以在 UI 元素範例 GitHub 存放區中找到。
請試玩看看,並告訴我們使用情形。