CSS 滾動捕捉允許使用者完成滾動之後将視口鎖定到某個元素的位置。非常适合用來建立下面這樣的應用:
基本使用
實作滾動捕捉主要依靠兩個屬性:容器元素的
scroll-snap-type
屬性,以及子元素的
scroll-snap-align
屬性。最基本的使用方式如下:
<div class="container">
<section class="child"></section>
<section class="child"></section>
<section class="child"></section>
<p>...</p>
</div>
.container {
scroll-snap-type: y mandatory;
}
.child {
scroll-snap-align: start;
}
第一版中定義的屬性與此不同,是通過
repeat
關鍵字實作的。
.container {
/* OLD */
scroll-snap-points-y: repeat(300px);
}
這種定義捕捉的方式很局限,隻在間距均勻的捕捉點(evenly-spaced snap points)場景下有效,如果個别子元素尺寸與其他的不一樣,就會有問題。
在撰寫本文時,Firefox、Internet Explorer 和 Edge 都支援較早版本的規範,而 Chrome(69+)和 Safari 支援較新的基于元素(element-based)的方法。
你可以同時使用這兩種方法(如果布局允許),以便支援這兩類浏覽器:
.container {
scroll-snap-type: mandatory;
scroll-snap-points-y: repeat(300px);
scroll-snap-type: y mandatory;
}
.child {
scroll-snap-align: start;
}
但我認為使用基于元素的文法才是更靈活的選擇,你可以一個下載下傳 polyfill 來支援尚不支援它的浏覽器。本文中後續的例子我也都是使用最新的文法寫的。
注意:
不幸的是,polyfill 并沒有附帶浏覽器包,如果不使用建構工具的話,使用起來會有點麻煩。我發現一個最簡單的方法是連結到 bundle.run 上的腳本,并在加載 DOM 後使用
進行 polyfill 初始化。還需要指出的是,此 polyfill 僅支援基于元素的文法,而不支援
cssScrollSnapPolyfill()
方法。
repeat
容器屬性
滾動捕捉需要同時設定父元素(容器)屬性和子元素屬性。這與 Flex 布局和網格布局裡概念類型。
下面分别介紹。
scroll-snap-type: “mandatory” vs. “proximity”
mandatory
值表示,在使用者停止滾動時,
浏覽器必須滾動到一個捕捉點;
proximity
屬性就沒有嚴格——除非目前滾動的位置合适,否則
不會強制浏覽器滾動到捕捉點。以我的經驗,當滾動停止在距離某個捕捉點幾百像素内時,捕捉才會發生。
我在工作中發現,
mandatory
能提供更一緻的使用者體驗。但是,規範中也指出,在遇到内容元素比滾動容器還高的情況,使用這個值就有點危險。
如果這裡的容器元素設定了
scroll-snap-type: mandatory
,它總是會吸附到元素的頂部或下面元素的頂部,使得這個超高元素的中間部分内容是難完整檢視。
scroll-padding
預設情況下,内容元素會吸附到容器的最邊緣。我們可以通過設定容器的
scroll-padding
屬性來做修改。它的文法與
padding
屬性一樣。
如果布局中出現有可能妨礙内容元素展示的物件(比如,固定标題),使用它就比較有用。
子元素屬性
現在再來看看,可在子元素上應用的屬性。
scroll-snap-align
通過這個屬性,可以指定元素的哪一部分吸附到容器上。可能的取值有三個:
start
、
center
和
end
。
這些值的含義是相對于滾動方向。如果是垂直滾動,
start
指的是元素的頂部邊緣。如果你水準滾動,它指的是左邊緣。
center
和
end
屬性值與此同理。屬性還支援兩個值的文法,分别指定水準和垂直方向上的捕捉對齊方向。
注:以 scroll-snap-align: start end;
為例,此處第一個值表示垂直滾動時的對齊點,第二個值則表示水準滾動。更多内容,可以參考 MDN 文檔。
scroll-snap-stop:“normal” vs. “always”
預設情況下,滾動捕捉隻會在使用者停止滾動時發生,這表示如果滾動過猛,中間可能會跳過幾個捕捉點,然後才會停止。可以通過給子元素設定
scroll-snap-stop: always
來改變這一行為。這會将強制滾動容器在使用者繼續滾動之前停留在在就近的一個元素上。
在寫這篇文章的時候,還沒有浏覽器原生支援
scroll-snap-stop
屬性,盡管 Chrome 有一個 tracking bug 在。
讓我們看一些使用滾動捕捉的例子。
例子一:垂直滾動
實作垂直吸附滾動隻需用到幾行 CSS。首先,我們告訴容器沿其垂直軸捕捉:
.container {
scroll-snap-type: y mandatory;
}
然後,定義吸附點(snap points)。這裡,我們指定清單元素的頂部是吸附點:
.child {
scroll-snap-align: start;
}
例子二:水準滑塊
為了實作水準滑塊,我們告訴容器沿其 x 軸對齊。我們還使用
scroll-padding
確定子元素在容器中心處對齊。
.container {
scroll-snap-type: x mandatory;
scroll-padding: 50%;
}
然後,我們告訴容器要吸附到哪個點。為了将圖庫(gallery)居中,我們将每個元素的中心點定為吸附點。
.child {
scroll-snap-align: center;
}
例子三:垂直全屏滾動
我們可以直接在 HTML 元素上設定捕捉點:
html { /* body won't work ¯_(ツ)_/¯ */
scroll-snap-type: y mandatory;
}
然後,使每個部分的大小與視口一樣,并将頂部邊緣定義為捕捉點: section {
height: 100vh;
width: 100vw;
scroll-snap-align: start;
}
效果 :
例子三:水準全屏滾動
**這與垂直版本具有相類似,不夠捕捉點位于 X 軸上。
body {
scroll-snap-type: x mandatory;
}
section {
height: 100vh;
width: 100vw;
scroll-snap-align: start;
}
效果(demo) :
例子五:2D 圖檔網格
滾動捕捉可以同時在兩個方向上進行。
.container {
scroll-snap-type: both mandatory;
}
然後,我們将每個圖塊的左上角定義為捕捉點:
.tile {
scroll-snap-align: start;
}
效果 :
關于在使用者體驗上的一點想法
基于 CSS 的滾動捕捉的偉大之處在于,你無需直接控制滾動位置。取而代之的是,隻需向浏覽器提供一個位置清單,系統會自動以适合目前平台的首選項方式做捕捉處理。這表示我們縮寫的滾動捕獲效果感覺很比對系統風格(使用了使用相同的動畫效果)。
對我來說,這是 CSS 滾動捕捉優于提供類似功能的 JavaScript 庫的主要優勢。
根據我的經驗,這在移動裝置上效果還不錯。也許是因為滾動捕捉已經是移動平台上本機 UI 的一部分(iOS 和Android 裝置上主屏的 App 圖表,本質上就是帶有捕捉點的水準滑塊)。Android 的 Chrome 上的互動特别好,因為它感覺像是自然的滾動效果,但視口總是碰巧剛好的地方停止滾動:
在這背後肯定有一些夢幻的數學公式使之成為現實。感謝 CSS 滾動捕捉,為我們免費提供了這麼好用的效果。淡然,這種效果也不要濫用在不合适的其他地方,我們可以把當做一種增強效果是哦也能,比如幻燈片滾動效果這些,似乎是不錯的選擇。當然,可能也有其他方面的潛力,我沒想到的。
總結
CSS 捕捉點使你能夠與浏覽器的本機滾動互動挂鈎,令界面達到無縫流暢的效果。随着可能的 JavaScript API 出現,這些功能将變得更加強大。
從最零基礎開始的的HTML+CSS+JavaScript。jQuery,Ajax,node,angular架構等到移動端HTML5的項目實戰【視訊+工具+系統路線圖】都有整理,線上解析,學習指導,點:【WEB前端學習圈⑤】