本文翻自Scroll Bouncing On Your Websites,拜讀以後收穫頗多,結合本身的理解,將該文章翻成中文,一方面加深理解另外一方面好文共享。javascript
本文介紹了不一樣瀏覽器上彈簧滾動(即scroll bouncing)特效及實現,並回顧了網上幾種常見的解決方案,順便介紹了下近來實現的css屬性 overscroll-behavior。但願讀過以後能對構建和設計帶有fixed元素的頁面有所幫助css
彈簧特效(一樣被叫作滑動橡皮筋特效或彈性滾動),常常發生於下面的場景:html
當你不想看到fixed的元素跟着頁面移動時,彈簧特效就不那麼使人愉快了。例如:咱們但願頁面上有位置固定的header和footer、或者須要一個fixed的菜單、滾動過程當中捕獲頁面的具體位置、不但願頂部或者底部有額外的滾動。這時候就須要咱們去看一下有什麼方案去解決這類頁面頂/底部的彈簧特效了。java
假如入咱們有下面這個頁面,底部有一個固定且不能移動的footer,同時頁面其餘內容能夠滾動。看起來以下: 若是是在非觸摸屏和觸摸板的Firefox和其餘瀏覽器上,表現是符合預期,可是當咱們使用mac上的chrome時,當用觸摸板scroll到最下部時,事情就有點不同了。 jquery
雖然設置了footer爲fixed=>bottom的css,可是這個橡皮筋特效確實有點猝不及防。
讓咱們看看position:fixed到底幾個意思:
According to the CSS 2.1 Specification, when a 「box」 (in this case, the dark blue footer) is fixed, it is 「fixed with respect to the viewport and does not move when scrolled.」
根據CSS 2.1 規範: 當一個box(這裏顯然是footer)被設置爲fixed,它將根據viewport定位而且在滾動過程當中不移動。ios
顯然上面的效果是預期以外的。
爲了使文章更加完整,把在移動端Edge、移動端Safari和桌面Safari的效果都進行了嘗試,確實在firefox和chrome上的表現是不一樣的。在不一樣平臺上開發相同效果的滑動確實是一件挑戰性的事情。web
對於咱們來講最早出來的想法確定是簡單快捷的方式,那麼針對這種狀況,首選固然是css來單獨處理。所以選擇下面的方式來嘗試。測試瀏覽器包括win10和mac上的chrome、firefox、safari以及Edge和移動端safari,瀏覽器的版本都是2018最新版本。頁面結構以下:chrome
<html>
<body>
<div class="body-container">
<div class="color-picker-main-container">
</div>
</div>
<footer>
</footer>
</body>
複製代碼
使用absolute來定位footer,而後html相對定位height100%,以便footer始終在下方固定,content的高度就是100%減去footer的高度。固然也能夠設置padding-bottom來代替calc,同時設置body-containe爲100%防止footer重複。語言比較蒼白,看代碼就完了:瀏覽器
html {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
body {
width: 100%;
margin: 0;
font-family: sans-serif;
height: 100%;
overflow: hidden;
}
.body-container {
height: calc(100% - 100px);
overflow: auto;
}
.color-picker-main-container {
width: 100%;
font-size: 22px;
padding-bottom: 10px;
}
footer {
position: absolute;
bottom: 0;
height: 100px;
width: 100%;
}
複製代碼
這種方式和原來fixed的方式幾乎同樣。差異在於該方式滑動的部分再也不是整個頁面而是content內容,不包括footer。這種方式最大的問題在於移動端的safari上,不只僅是content,footer也會跟着一塊兒滑動。。。當滑動很快的時候表現簡直是災難。以下圖 app
此外,另外一個不想看到的狀況也出現了,當滑來滑去的嘗試的時候,發現此時的滑動性能有點差。
由於咱們設置滑動容器的高度爲它自己的100%,這樣就阻礙了ios上的momentum-based scrolling,
這裏的momentum-based scrolling,我沒有很好的語言來翻譯,簡稱爲阻尼滑動吧
簡單而言就是移動設備上增長的一種旨在提高頁面滑動性能的功能,比較明顯的體現就是當你的手指輕觸觸碰設備表面時,頁面自身開始滑動,當手指中止滑動以後頁面還會順勢滑動一會。更多瞭解請轉。我確定是但願有這種效果的,因此要遠離設置滑動元素height100%。
在繼續其餘的嘗試以前,咱們先慢下來想想當前的狀態。原先的fixed定位存在橡皮筋的問題,上面的將其轉換爲absolute+relative的話沒有了阻尼滑動。若是想要阻尼滑動,那麼內容部分的height就不能設置爲100%。那麼是否能夠不去顯式設置height爲100%呢。
html {
width: 100%;
position: fixed;
overflow: hidden;
}
body {
width: 100%;
margin: 0;
font-family: sans-serif;
position: fixed;
overflow: hidden;
}
.body-container {
width: 100vw;
height: calc(100vh - 100px);
overflow-y: auto;
// Use momentum-based scrolling on WebKit-based touch devices
-webkit-overflow-scrolling: touch;
}
.color-picker-main-container {
width: 100%;
font-size: 22px;
padding-bottom: 10px;
}
footer {
position: fixed;
bottom: 0;
height: 100px;
width: 100%;
}
複製代碼
這裏設置html,body均爲fixed、overflow: hidden。footer一樣爲fixed。
在須要滾動的body-container內容區域設置其高度爲100vh-footer的高度,
同時增長-webkit-overflow-scrolling: touch;開啓阻尼滑動支持。
效果會怎麼樣呢。 mac上的Chrome和Firefox和上一種方式標表現形式是同樣的,這種方式的優勢就是再也不須要100% height,
因此 momentum-based scrolling表現的還不錯,然而在Safari,footer不見了。。。
在iOS的 Safari上,footer變短,而且底部有了個額外的間隔。一樣,當滾到底部的時候,滾動頁面的能力消失了。
在上面代碼裏-webkit-overflow-scrolling: touch;給指定元素增長 momentum-based scrolling 的能力。不過該屬性在MDN中標識是非標準的,兼容性有待考慮,因此也只能拋棄它了。
另外一種方案以下:
html {
position: fixed;
height: 100%;
overflow: hidden;
}
body {
font-family: sans-serif;
margin: 0;
width: 100vw;
height: 100vh;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
.color-picker-main-container {
width: 100%;
font-size: 22px;
padding-bottom: 110px;
}
footer {
position: fixed;
}
複製代碼
這種方式在不一樣的桌面瀏覽器上表現是不錯的,阻尼滑動、footer固定而且不跟隨移動。可是這種方式的缺點在於在iOS Safari 上能夠發現footer有輕微抖動
而且當你滑動的時候能夠看到content在footer下面。
既然上面的方式都有些瑕疵,那麼咱們仍是試試js來解決吧。
首先聲明我不推薦而且建議儘可能避免使用該方式。依據原做者的經驗,應該存在更爲優雅和簡介的html+css方式。 不過已經花費了不少時間去解決該問題,去看看使用js是否有更好的方式也不會有什麼損失。
一種避免滑動彈簧的方式是阻止window或者document的touchmove或touchstart事件。思路是阻止外層window的tocuch事件,只容許content部分的touch。代碼以下:
// Prevents window from moving on touch on older browsers.
window.addEventListener('touchmove', function (event) {
event.preventDefault()
}, false)
// Allows content to move on touch.
document.querySelector('.body-container').addEventListener('touchmove', function (event) {
event.stopPropagation()
}, false)
複製代碼
我嘗試了不少方式盡力使滑動表現良好,阻止widow的touchmove和阻止document的沒什麼區別,我也嘗試使用touchstart和touchmove來控制滑動,
不過這兩種方式也沒什麼區別。後來發現出於性能的考慮,不該該這種方式來使用event.preventDefault(),應該設置將false做爲passive的選項來設置。
// Prevents window from moving on touch on newer browsers.
window.addEventListener('touchmove', function (event) {
event.preventDefault()
}, {passive: false})
複製代碼
另外可使用iNoBounce來幫助本身,該庫目的就是解決ios上web應用滑動時的彈簧效應。須要提一下的時,使用該庫解決上面問題時要加上-webkit-overflow-scrolling。
另外我在結尾時提到的簡潔方法和其有殊途同歸之妙,能夠對比一下二者。
嘗試那麼多方案以後,我發現了css的一個屬性overscroll-behavior,該屬性CSS屬性在2017年12月和2018年3月分別在Chrome 6三、Firefox 59中實現。
根據mdn的定義:容許你控制瀏覽器的滑動溢出的行爲--當到達滾動區域的邊邊界時會發生的行爲。這就是最後的一種方案。
須要作的僅僅是在body設置overscroll-behavior:none,並設置footer爲fixed,相比於沒有foter,整個頁面應用momentum-based scrolling是能夠接受的。 更加客觀的是Edge正在開發中,將來可期。
再次感謝原做者William Lim,提供了比較豐富滑動橡皮筋特效的解決思路。才疏學淺,有些翻譯不到位的地方多請指正,詳情異步原文