巧用 CSS sticky 實(shí)現(xiàn)返回頂部功能
經(jīng)常在某些文檔中或者文章中可以看到這樣一個(gè)"返回頂部"的功能,具體有兩個(gè)交互:
- 只有滾動(dòng)一定距離才會(huì)出現(xiàn),返回到頂部重新隱藏
- 點(diǎn)擊會(huì)返回到頂部
比如 LuLu UI[1]
又是點(diǎn)擊的,又是滾動(dòng)的,看著好像必須要借助 JavaScript 了,其實(shí)也可不必,經(jīng)過我的一番琢磨,僅僅使用一點(diǎn)點(diǎn) CSS 就能實(shí)現(xiàn)這樣的交互效果,一起看看吧!
一、粘性滾動(dòng)
這里就需要一點(diǎn)點(diǎn)想象了。比如這里滾動(dòng)到一定距離才出現(xiàn),是不是有點(diǎn)類似 CSS sticky[2] 的概念?只不過 sticky 的一般作用是滾動(dòng)到一定距離,然后就固定到某一位置,mdn 解釋如下
元素根據(jù)正常文檔流進(jìn)行定位,然后相對(duì)它的*最近滾動(dòng)祖先(nearest scrolling ancestor)*和 containing block[3] (最近塊級(jí)祖先 nearest block-level ancestor),包括table-related元素,基于top, right, bottom, 和 left的值進(jìn)行偏移。偏移值不會(huì)影響任何其他元素的位置。
雖然和我們需要的交互有點(diǎn)出路,但是仍然可以通過一定的“技巧”聯(lián)合起來,先簡(jiǎn)單實(shí)現(xiàn)一下布局:
- <a class="back"></a>
- <article>
- ...很多內(nèi)容
- </article>
這里注意需要將.back 放在前面,不然沒法觸發(fā)粘性定位,然后給 .back加上 sticky 定位。
- .back{
- position: sticky;
- display: block;
- top: 0;
- border-radius: 50%;
- background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E %3Cpath fill='%23ffffff' d='M177 159.7l136 136c9.4 9.4 9.4 24.6 0 33.9l-22.6 22.6c-9.4 9.4-24.6 9.4-33.9 0L160 255.9l-96.4 96.4c-9.4 9.4-24.6 9.4-33.9 0L7 329.7c-9.4-9.4-9.4-24.6 0-33.9l136-136c9.4-9.5 24.6-9.5 34-.1z'%3E%3C/path%3E %3C/svg%3E") center no-repeat dodgerblue;
- background-size: 50%;
- width: 50px;
- height: 50px;
- }
由于這里的 top 給的為 0,所以返回按鈕在滾動(dòng)過程中是貼在頂部的,如下:
這時(shí),如果把 top 改為一個(gè)負(fù)值會(huì)怎么樣呢?
- .back{
- /**/
- position: sticky;
- top: -30px;
- }
可以看到,按鈕會(huì)在超出屏幕 -30px 的地方固定,如下:
接著,我們把.back向下偏移整個(gè)屏幕距離,也就是 100vh。
- .back{
- /**/
- position: sticky;
- top: -30px;
- transform: translateY(100vh);
- }
這樣,和我們需要的效果已經(jīng)很接近了,只是最后只出現(xiàn)了一部分,如下:
原理示意如下:
最后,把剛才的top設(shè)置的更小一些,直到.back可以完全出現(xiàn),比如設(shè)置 -60px。
- .back{
- /**/
- position: sticky;
- top: -60px;
- transform: translateY(100vh);
- }
這樣基本上就完成了,不過還有一些問題,接著往下看。
二、右下角的處理
上面的實(shí)現(xiàn)其實(shí)還有兩個(gè)布局問題需要優(yōu)化:
- 按鈕本身占據(jù)了一定的空間
- 按鈕一般位于右下角
一般為了讓一個(gè)元素不占空間,可能想到的方法是設(shè)置絕對(duì)定位。但是這里由于設(shè)置了 position: sticky,所以肯定不能再設(shè)置絕對(duì)定位了。除此之外,我們還可以采用浮動(dòng)float,可以很輕易的解決以上兩個(gè)布局問題
- .back{
- /***/
- float: right
- }
設(shè)置右浮動(dòng)有兩個(gè)好處,一是脫離文檔流,不影響高度,二是實(shí)現(xiàn)居右效果,實(shí)際效果如下:
其實(shí)到了這里,還是有個(gè)小問題的,當(dāng)頭部的文字比較多時(shí),可以很清楚的看到右環(huán)繞效果,如下:
如何處理呢?很簡(jiǎn)單,加一個(gè)負(fù)的margin就可以了。
- .back{
- /***/
- float: right;
- margin-top:-50px;/*自身高度*/
- }
但是又出現(xiàn)了新的問題,底下的按鈕又漏出來了。
由于 top已經(jīng)被sticky占用,現(xiàn)在改變按鈕位置就只能靠transform了,這里可以用calc進(jìn)行計(jì)算,同時(shí)top也要相應(yīng)減少自身高度。
- .back{
- /***/
- float: right; z margin-top:-50px;/*自身高度*/
- top: -110px; /*60 + 50*/
- transform: translateY(calc(100vh + 50px));
- }
完美!
三、返回頂部
返回頂部就比較容易了,一般可以通過href='#'就可以實(shí)現(xiàn),當(dāng)然,為了平滑的滾動(dòng),可以加上scroll-behavior: smooth。
- html, body {
- scroll-behavior:smooth;
- }
實(shí)際效果如下:
最后附上完整代碼,非常少。
- html,body{
- scroll-behavior: smooth;
- }
- .back{
- position: sticky;
- float: right;
- top: -110px;
- margin-top: -50px;
- border-radius: 50%;
- background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E %3Cpath fill='%23ffffff' d='M177 159.7l136 136c9.4 9.4 9.4 24.6 0 33.9l-22.6 22.6c-9.4 9.4-24.6 9.4-33.9 0L160 255.9l-96.4 96.4c-9.4 9.4-24.6 9.4-33.9 0L7 329.7c-9.4-9.4-9.4-24.6 0-33.9l136-136c9.4-9.5 24.6-9.5 34-.1z'%3E%3C/path%3E %3C/svg%3E") center no-repeat dodgerblue;
- background-size: 50%;
- width: 50px;
- height: 50px;
- transform: translateY(calc(100vh + 50px));
- }
HTML 只需要在起始地方加一個(gè)標(biāo)簽即可。
- <body>
- <a class="back" href="#"></a><!--添加再這里就行了-->
- <article>
- ...很多內(nèi)容
- </article>
- </body>
線上代碼可訪問 back-top \(codepen.io\)[4]
四、總結(jié)和說明
以上借助 CSS sticky 實(shí)現(xiàn)了一個(gè)自動(dòng)顯示返回按鈕的小交互,本身代碼量并不復(fù)雜,其實(shí)是一點(diǎn)點(diǎn)想象力,把比較相似的效果聯(lián)想起來,多多嘗試,可能會(huì)帶來不一樣的解決方案。下面總結(jié)一下實(shí)現(xiàn)要點(diǎn):
- CSS sticky 可以實(shí)現(xiàn)粘性滾動(dòng)效果,可以設(shè)置負(fù)值
- transformY(100vh)可以偏移1屏幕高度,不影響占位
- 浮動(dòng)可以脫離文檔流,不影響高度
- 負(fù)的 margin 可以抵消浮動(dòng)的環(huán)繞效果
- scroll-behavior: smooth 可以實(shí)現(xiàn)平滑滾動(dòng)
- 兼容性取決于 sticky,不兼容IE
還算是一個(gè)比較實(shí)用的小功能,雖然 JS 也能實(shí)現(xiàn),但是能用 CSS 實(shí)現(xiàn)的何必麻煩 JS 呢?相比 JS 而言,CSS 使用起來簡(jiǎn)單方便,也無需考慮加載問題,幾乎零成本。