當前位置:
首頁 > 科技 > 高性能視差滾動

高性能視差滾動

前言

8月2號早讀文章由@杜夢傑翻譯分享。

正文從這開始~

明智地使用視差滾動效果,可以使你的web app更加精妙。但問題是實現高效的視差滾動是很有挑戰性的。本文中我們會討論一個既高性能又可以跨瀏覽器工作的解決方案。

TL;DR

不要使用滾動事件scroll events或者背景位置background-position來實現視差滾動動畫。

使用三維變形CSS 3D transforms創造更加精準的是視差效果。

對於移動版Safari瀏覽器使用position: sticky屬性確保視差效果被正確傳遞。

如果你想要一個開箱即用的解決方案,請前往UI Element Samples GitHub repo並閱讀Parallax helper JS。在這個倉庫里可以看到一個在線的demo。

有問題的視差滾動

首先,我們來看一下兩種最常見的實現視差效果的方法,並著重了解為什麼它們不適合用來實現視差滾動。

Bad: 利用滾動事件

視差滾動的核心要求是滾動配對scroll-coupled:每當頁面的滾動位置scroll position改變,元素的位置都要更新。儘管聽起來很簡單,但是現代瀏覽器的一個重要機制就是非同步工作work asynchronously。這個機制對於滾動事件也適用。在大多數瀏覽器中滾動事件都是以最好性能best effort為原則來進行傳遞的,並不保證在滾動動畫的每一幀都會進行傳遞!

通過這一關鍵信息我們可以知道,對於根據滾動事件來移動元素的情況,要避免使用基於JSJavaScript-based的解決方案:JS並不保證視差滾動與頁面滾動位置同步。在舊版本的移動版Safari中,滾動事件是在滾動結束時才進行傳遞的,這樣根本不可能實現基於JS的滾動效果。在最新的一些版本中滾動事件確實是在動畫過程中傳遞的,但與Chrome類似,是基於最好性能best effort原則的。一旦主線程繁忙,滾動事件就不會立刻傳遞,這就意味著視差效果將會被破壞。

Bad: 更新背景位置

我們要盡量避免的另一種情況是每一幀都渲染。有很多方法試圖改變背景圖片位置background-position來實現視差效果,這樣會導致瀏覽器在滾動時重繪頁面的相關部分,這是非常耗費性能的,會對動畫造成明顯的破壞。

如果想要保證視差滾動,我們需要一些不依賴於滾動事件的動態屬性accelerated property。

CSS 3D

Scott Kellum和Keith Clark在使用CSS 3D實現視差滾動方面卓有成就,他們使用的方法如下:

將要滾動的容器元素屬性設置為overflow-y: scroll(和overflow-x:hidden)。

對同一個元素應用perspective值,並將perspective-origin設置為top left或0 0。

對容器元素的子元素應用Z軸變形,通過縮放子元素實現視差效果,注意不要影響到它們在屏幕上的大小。

這種實現方式的代碼如下:

.container{

width:100%;

height:100%;

overflow-x:hidden;

overflow-y:scroll;

perspective:1px;

perspective-origin:;

}

.parallax-child{

transform-origin:;

transform:translateZ(-2px)scale(3);

}

對應的HTML 如下:

調整縮放以符合透視

將子元素向後推會使其在透視上占的比例更小。我們可以用(透視距離-移動距離)/ 透視距離(perspective - distance) / perspective這個公式計算出元素需要放大多少。我們需要讓元素視差滾動但是還要保持大小,所以需要將其以這種方式縮放,而不是置之不理。

在上面的代碼中,透視距離是1px,視差子元素的Z軸透視距離是-2px。這樣的話元素需要放大三倍,正如代碼中所寫的:scale(3)。

對於任何沒有應用translateZ值的內容,其移動距離可以用0來代替。就是(perspective - 0) / perspective,其值為1,既不放大也不縮小。

工作原理

理解這種方法的原理很重要,因為我們馬上用到這些知識。滾動實際上也是一種變換transform,這就是為什麼滾動可以被加速;它主要涉及到改變GPU周圍的層。在典型的滾動中,即不涉及透視,滾動元素和和它的子元素的改變是1:1的。如果一個元素向下滾動300px,那子元素向上變換同樣的值300px。

但是,對滾動元素應用透視值擾亂了這個過程;它會改變滾動變換的基礎矩陣。滾動300px距離可能子元素只會移動150px,取決於你設定的perspective和translateZ值。如果某元素的translateZ值是0,那它就會以1:1的比例滾動(正常模式),但是在Z軸上遠離透視原點的子元素會以不同的比率滾動!這樣就產生了視差滾動。非常重要的是,這一過程作為瀏覽器內部滾動機制一部分自動處理,無需監聽滾動事件或改變背景位置。

美中不足:移動版Safari

每種效果都有需要注意的地方,對於變換而言,一個重要注意事項是關於保護子元素的3D效果的。如果在透視元素和它的視差子元素之間的層級內有其他元素,那麼3D透視將被「展平」flattened(降維打擊),意味著效果將丟失。

在上面的HTML中,.parallax-container是個新元素,它實際上會展平perspective值,使視差滾動失效。大部分情況下,解決方案相當直接:為元素添加transform-style: preserve-3d屬性,使其能傳遞任何已應用於DOM樹中的3D 效果。

.parallax-container{

transform-style:preserve-3d;

}

但是在移動版Safari上,情況就有些複雜了。為容器元素應用overflow-y: scroll技術上是可行的,但要以犧牲滾動元素為代價。解決方法是添加-webkit-overflow-scrolling: touch,但這也會展平perspective,得不到任何視差效果。

從漸進增強的角度來看,這可能不是什麼大問題。如果某些情況下不能視差滾動,我們的應用仍然可以正常工作,但如果能找出一個解決方案也是很好的。

救世主來了position: sticky!

position: sticky(還沒有標準的中文譯名,本文統一翻譯成粘性定位)屬性允許元素在滾動時附著在視口或給定元素的頂部。這個屬性和大多數position屬性一樣很笨重,但是卻有一個很好的點:

粘性定位的元素與相對定位的元素定位方式類似,但是其偏移量是以最近的有滾動框的祖先元素為參照的,如果祖先元素都沒有滾動框則以視口為參照。-CSS Positioned Layout Module Level 3

乍看之下好像沒什麼用,這個信息的關鍵就在於對元素stickiness計算的解讀:「其偏移量是以最近的有滾動框的祖先元素為參照的」。換句話說,粘性定位元素的移動距離(使其附著在其他元素或視口上的距離)是在其他變換執行之前就計算好的,而不是之後。這就意味著,就像之前的例子一樣,如果計算出的偏移值是300px,就有機會通過使用透視(或任何其他變換)在應用到元素上之前處理偏移值。

通過為視差滾動元素應用position: -webkit-sticky屬性,可以有效的逆轉-webkit-overflow-scrolling: touch的展平效果。這保證了視差元素會以最近的具有滾動框的祖先元素作為參照,本情況下是.container元素。然後,和之前類似,.parallax-container元素應用perspective值,這會改變計算出的滾動偏移值並創造出視差效果。

.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的這個demo,展示了有無position: sticky的元素行為上的差異。要看出效果需要使用Chrome或Safari。

BUG和解決方案

與任何事情一樣,仍有許多小問題需要處理:

各瀏覽器對粘性的支持性不一致。Chrome是支持的,Edge完全不支持,在Firefox上,當粘性定位和透視變換perspevtive transform組合使用時有渲染bug。如果需要的話,增加一句額外的代碼position: -webkit-sticky就能解決移動版Safari的兼容問題,性價比還是很高的。

視差效果在Edge里不會執行。Edge瀏覽器在OS層來處理滾動,這通常是個好事,但是在這種情況下則阻止檢測到滾動中的透視變化。可以加一個固定定位元素來修復,參考這篇文章。

頁面體積變得太大了!許多瀏覽器在計算頁面體積時會考慮到縮放,但可惜的是Chrome和Safari並沒有考慮到透視。所以如果元素被放大了三倍,即使在經過透視處理後其實沒有放大,你也會看到滾動條。這個問題可以通過將元素的變形原點放到右下角transform-origin: bottom right來解決, 原理是元素放大的部分進入到了滾動區域負區域negative region內(通常實在左上方),滾動區域是不會看到負區域的內容的。

總結

如果能合理的使用的話,視差滾動其實是個非常有趣的特效。如你所見,我們可以高性能、跨瀏覽器地將其實現。由於視差滾動需要一點數學知識和少量的模板來得到想要的效果,我們為你們打包好了一個幫助庫和示例,鏈接在這裡 UI Element Samples GitHub repo。

關於本文

譯者:@杜夢傑

譯文:https://dumengjie.github.io/2017/07/24/譯-高性能視差滾動/

作者:@Paul Lewis, Robert Flack

原文:https://developers.google.com/web/updates/2016/12/performant-parallaxing

點擊展開全文

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 前端早讀課 的精彩文章:

深入淺出React高階組件
阿里天貓杭州長期求 資深前端開發/專家
6種組織CSS的方法
手機QQ里的註冊那些事兒
請讓我再次的任性,因為需要你的幫助

TAG:前端早讀課 |

您可能感興趣

運用實時可視化與高性能渲染技術打造高品質影視內容
AI驅動的山石網科 用高性能與高智能不斷實現安全進化
高性能與高顏值並存的動感健身車,在家也能高效做有氧
距離柔性電視與高性能的可穿戴智能設備更進一步
高性能、高可用平台架構的演變過程
潛心研究更高性能動力電池
移動固態硬碟好用處:改造高性能電腦
單伺服器高性能模式
基於磁渦流結構的高性能磁感測器實現
無懼高溫 高性能汽車冷卻液導購
顏值性能當道,安卓高性能平板酷比魔方X評測
全新極具性價比的高性能流媒體編碼器支持實時多流傳輸
如何用低技術實現高性能
日本在超大容量、低耗電、高性能非易失性存儲器製造上邁進一步
高顏值高品質高性能,3高顯示器快來體驗
勁諾高性能工具磨削油
輕奢高性能輕薄本已成為市場主力 英特爾創新技術推動形態變革
輕薄便攜高性能,機械革命S1體驗評測
高顏值、高像素、高性能
讓遊戲操作更順暢 高性能平板推薦