為什麼要寫這篇文章
Sticky
也不是新知識點了,寫這篇文章的原因是由于最近在實作效果的過程中,發現我對
Sticky
的了解有偏差,代碼執行結果不如預期。決定寫篇文章重新學習一次。
什麼是 Sticky
Sticky
(MDN 翻譯成粘性效果)是
CSS
屬性
position
中的一個可選值。跟我們用得比較多的
static
,
fixed
,
relative
,
absolute
一樣,用來描述元素的定位方式。
從效果上看,
Sticky
像是混合體,頁面滑動到“臨界點”之前表現為
relative
, 到達“臨界點”時表現為
fixed
。
如何使用
使用
CSS Sticky
隻需要兩個條件。
position: sticky;
top: 0; // right/bottom/left 任一有效值,甚至可以為負像素值
複制代碼
top:0
意思是當元素滑動到距離視口 0px 時再繼續滑動,元素吸頂。可以在 這裡 看效果(試試看修改 top 值)
對比 JS 的實作方案
沒有
CSS Sticky
之前,類似的效果都是使用 JS 實作。大緻步驟如下:
- 監聽滾動事件,計算目标元素距離視口的距離。
- 距離不滿足條件時,按兵不動。
- 距離滿足條件時,建立占位元素,修改目标元素定位方式為
。fixed
window.addEventListener('scroll', () => {
const rect = elem.getBoundingClientRect();
// 計算目标元素和視口的距離
})
複制代碼
在 npm 上搜 sticky 關鍵字,也有很多優秀的包可以使用。以 react-sticky 為例,滿足條件時會建立
placeholder
元素(防止頁面抖動),同時讓
header
定位為
fixed
。
右邊是
Chrome Dev-Tools
的
layers
面闆,藍色部分為生成的
placeholder
。
兩種方案的火焰圖對比(為了放大效果,我把
cpu
調慢了 6 倍)
CSS 方案
使用
CSS Sticky
,工作都交給
GPU
了,不占用
JS
主線程的資源,在移動端上異常流暢。
React Sticky
由于需要在
scroll event
回調中不斷調用
getBoundingClientRect
,而
getBoundingClientRect
又會觸發頁面重排重繪,稍不留神就掉幀卡頓。僅僅為了實作這個效果(頁面上沒有其他内容)大動幹戈成本效益很低。
結論是:實作
Sticky
效果,優先選擇
CSS Sticky
了解上的偏差
1. 隻在 Containing Block 内有效。
修改例子,用一個 div 把
Sticky Header
包裹起來,發現
Sticky
效果失效了!!!
<div class="wrapper">
<header>Sticky Header</header>
</div>
...
複制代碼
根據文檔,
Sticky
效果隻在 Containing Block 内有效,
Containing Block
滑出螢幕時,
Stickey Element
也跟着滑走。
修改
wrapper
的高度,看效果。
.wrapper {
height: 100px;
background-color: #e6e6e6;
}
複制代碼
多個
Sticky Element
放在一塊就有了前一個被後一個頂出去的特效,實際上并不是真的被頂出去,而是
Containing Block
把它拖走。
代碼看這裡
2. Overflow 會影響 Sticky
修改例子中的代碼,給
#root
加上
overflow: auto
#root {
overflow: auto;
}
複制代碼
Sticky
效果再次丢失(
overflow
設定為其他非
visible
的有效值也是同樣效果。)
看了很多相關的文檔,我的出來的結論是:
Sticky Element
的 offset 值是依據
nearest scrolling ancestor
(距離最近的滾動祖先) 計算的,如果沒有比對上的祖先元素,則使用視口作為參照物。
問題就出在
overflow-x
或者
overflow-y
其中任一為非
visible
則認為是要找的目标元素,而在滾動視窗的過程中,
Sticky Element
和 它找到的目标祖先元素的
offset
值一直沒有改變,是以
Sticky
不起作用。
對症下藥,讓滾動發生在被“誤比對”上的祖先元素内即可恢複
Sticky Effect
。
#root {
overflow: auto;
height: 100vh;
}
複制代碼
相容性
算上
prefixed
,目前
css sticky
手機端相容性達到 94.14%,如果你所做的業務需要照顧剩下 5.86% 的使用者,也可以使用 polyfill 或者
position: fixed
。
相關連結
- uxdesign.cc/position-st…
- codyhouse.co/ds/componen…
- medium.com/@elad/css-p…
- developer.mozilla.org/zh-CN/docs/…
- stackoverflow.com/questions/4…