改變用戶體驗的滾動新特性
來自專欄 W3cplus的故事187 人贊了文章
@evilmartians的《滾動的特性》一文介紹了目前有關於滾動相關的特性。今天我想花點時間重新整理一下,時至今日,CSS中為瀏覽器滾動提供的相關新特性究竟能給用戶帶來哪些新的體驗。
墨守成規的滾動條
一直以來,如果僅使用CSS來控制滾動條,我們只能借用overflow
屬性,比如:
overflow: auto | scroll;// 或者overflow-x: auto | scroll;// 或者overflow-y: auto | scroll;
當元素內容溢出容器之後,就會出現滾動條,其滾動效果如下:
這樣的效果大家或許已經習慣,覺得頁面或者元素滾動的效果就應該如此。甚至在Modal彈框中的滾動效果,大家覺得也應該是如此:
除了滾動體驗之外,視覺體驗相對而言更為糟糕,不同系統的瀏覽器渲染的滾動條風格也不是一致:
對於大部分同學而言,這些東西就應該是如此,不應該也不會有太多的變化。但對於有追求的設計師或者工程師來說,還是不想墨守成規,想給用戶帶來不一樣的體驗,不管是視覺外觀上,還是滾動流暢性。正因為如此,早期有很多優秀的JavaScript庫來改變這一切。
隨著技術的革新,就在現在或者未來的不久,我們可以採用純CSS的一些特性來改變這一切,讓用戶有一個更好的體驗。
改變滾動條外觀效果
前面提到過了,滾動條外觀的效果在不同系統存在不一樣的效果已是事實。雖然在Github有上百個庫可以讓開發者實現個性化的滾動外觀,但對於CSSer而言,能用純CSS解決的問題就決不使用JavaScript來解決。
在Webkit內核提供了-webkit-scrollbar
(由七個偽元素)屬性,可以輕易的幫助我們實現自定義(個性化)滾動條UI風格。在介紹這七個偽元素屬性之前,先來看一下滾動條的結構:
僅從上圖來看,是不是有一種似曾相識。是的,它的結構和我們平時看見的進度條或input[type="range"]
類似:
也就是說,我們可以像製作進度條一樣來處理滾動條UI效果。不同的是採用的CSS屬性不一樣。剛才也提到了,-webkit-scrollbar
提供了七個偽元素,通過這些偽元素,我們可以來定製滾動條外觀效果。這七個偽元素分別是:
::-webkit-scrollbar
:整個滾動條::-webkit-scrollbar-button
:滾動條上的按鈕(下下箭頭)::-webkit-scrollbar-thumb
:滾動條上的滾動滑塊::-webkit-scrollbar-track
:滾動條軌道::-webkit-scrollbar-track-piece
:滾動條沒有滑塊的軌道部分::-webkit-scrollbar-corner
:當同時有垂直和水平滾動條時交匯的部分::-webkit-resizer
:某些元素的交匯部分的部分樣式(類似textarea
的可拖動按鈕)
具體使用的時候非常的簡單。HTML結構和我們平時是一樣的:
<div class="scrollbar"> <div class="force-overflow"></div></div>
有關於滾動條的UI樣式風格都在.scrollbar
上設置,比如:
.scrollbar {}.scrollbar::-webkit-scrollbar{}.scrollbar::-webkit-scrollbar-thum{}.scrollbar::-webkit-scrollbar-track{}
在對應的-webkit-scrollbar
屬性寫上你想要的UI樣式風格,你就可以得到對應的滾動條UI樣式風格,比如下圖這樣的:
具體的代碼可以查看@akinjide在Codepen上寫的Demo:
https://codepen.io/airen/full/OZEpMj/有關於採用-webkit-scrollbar
屬性實現自定義滾動條UI效果的詳細教程,可以閱讀@Akinjide Bankole的教程。其他教程也可以查看下面的文章:
- Custom Scrollbars in WebKit
- Customize the Scrollbar
- How to customize the browser scrollbar with (WebKit) CSS
- 使用 CSS 自定義瀏覽器的滾動條
如果你不想燒腦,也可以使用在線生成器,比如@Darryl Huffman寫的Scrollbar Generator:
雖然能用純CSS搞定這一切,肯定也有不少同學會好奇,現在能否用於實際項目當中。還是來看看瀏覽器對其支持性吧:
顠紅的較少了,或許大家更為安心了。如果你想做得更好,那麼可以藉助在《五個最新的CSS特性以及如何使用它們》文中介紹的@supports
特性來做降級處理。如果瀏覽器支持-webkit-scrollbar
,那麼採用自定義屬性,如果不支持,則採用默認的滾動條風格。是不是很完美。
絲滑般的滾動
視差滾動效果,大家應該不會感到陌生吧:
視差效果曾經很多品牌網站都可見。有關於什麼是視差滾動效果,這裡不做詳細的闡述,如果你從未接觸過或者想了解如何製作視差滾動效果,建議你花點時間閱讀下面兩篇文章:
- Performant Parallaxing (譯文可以點擊這裡閱讀)
- Pure CSS Parallax Websites
同樣的,早期也湧現很多優秀的JavaScript庫來實現視差滾動效果。只不過這裡我們不是著重介紹怎麼製作視差滾動效果。而是來聊聊,怎麼通過使用CSS提供的滾動特性,實現絲滑般的滾動效果。而視差滾動效果是一個很好的示例。
先來看兩個效果,一個是普通的滾動效果,另一個是採用了新特性的滾動效果:
普通滾動效果,大家常見的滾動效果
優化後的滾動效果
怎麼實現後者這樣絲滑般的滾動效果,才是接下來的目的。很多同學可能首先會想到jQuery.scrollTo
方法或者類似的解決方法。但jQuery已經慢慢的淡出公眾眼線。
不過也有可能立馬想到原生的JavaScript,比如window.scrollTo(x, y)
方法。更優秀的程序員可能會藉助window.setTimeout()
、window.setInterval()
、Web Animation API 和window.requestAnimationFrame()
讓滾動效果更為平滑,也就是我們想要給用戶絲滑般的滾動體驗。比@pawelgrzybek提供的這段代碼:
function scrollIt(destination, duration = 200, easing = linear, callback) { const easings = { linear(t) { return t; }, easeInQuad(t) { return t * t; }, easeOutQuad(t) { return t * (2 - t); }, easeInOutQuad(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; }, easeInCubic(t) { return t * t * t; }, easeOutCubic(t) { return (--t) * t * t + 1; }, easeInOutCubic(t) { return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1; }, easeInQuart(t) { return t * t * t * t; }, easeOutQuart(t) { return 1 - (--t) * t * t * t; }, easeInOutQuart(t) { return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t; }, easeInQuint(t) { return t * t * t * t * t; }, easeOutQuint(t) { return 1 + (--t) * t * t * t * t; }, easeInOutQuint(t) { return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t; } }; const start = window.pageYOffset; const startTime = now in window.performance ? performance.now() : new Date().getTime(); const documentHeight = Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight); const windowHeight = window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName(body)[0].clientHeight; const destinationOffset = typeof destination === number ? destination : destination.offsetTop; const destinationOffsetToScroll = Math.round(documentHeight - destinationOffset < windowHeight ? documentHeight - windowHeight : destinationOffset); if (requestAnimationFrame in window === false) { window.scroll(0, destinationOffsetToScroll); if (callback) { callback(); } return; } function scroll() { const now = now in window.performance ? performance.now() : new Date().getTime(); const time = Math.min(1, ((now - startTime) / duration)); const timeFunction = easings[easing](time); window.scroll(0, Math.ceil((timeFunction * (destinationOffsetToScroll - start)) + start)); if (window.pageYOffset === destinationOffsetToScroll) { if (callback) { callback(); } return; } requestAnimationFrame(scroll); } scroll();}
調用方式很簡單,像下面這樣即可:
document.querySelector(.js-btn1).addEventListener(click, () => { scrollIt( document.querySelector(.js-section1), 300, easeOutQuad, () => console.log(`Just finished scrolling to ${window.pageYOffset}px`) );});
再簡單點還可以這樣使用:
document.querySelector(.js-btn50000).addEventListener(click, () => scrollIt(50000));
你將看到的效果如下:
https://codepen.io/airen/full/pZQgzM/除此之外,還可以使用JavaScript原生的API,比如:
window.scrollBy({ "behavior": "smooth", "left": left, "top": top});window.scrollTo({ "behavior": "smooth", "left": left, "top": top});
你也可以使用Element.scrollIntoView()
方法:
document.querySelector(.hello).scrollIntoView({ behavior: smooth });
上面看到的都是JavaScript方式來解決。還是回到原話題上,CSS能不能解決。回答是可以的。CSSOM View Module提供了一個新屬性scroll-behavior
。直接使用這個屬性就可以輕易的幫助我們實現絲滑般的滾動效果。
CSS屬性
scroll-behavior
為一個滾動框指定滾動行為,其他任何的滾動,例如那些由於用戶行為而產生的滾動,不受這個屬性的影響。在根元素中指定這個屬性時,它反而適用於視窗。
scroll-behavior
有兩個關鍵詞值:
auto
:滾動框立即滾動smooth
:滾動框通過一個用戶代理定義的時間段使用定義的時間函數來實現平穩的滾動,用戶代理平台應遵循約定,如果有的話
除此之外,其還有三個全局的值:inherit
、initial
和unset
(有關於這三個詞的詳細介紹可以點擊這裡)。
使用起來很簡單,只需要這個元素上使用scroll-behavior:smooth
。因此,很多時候為了讓頁面滾動更平滑,建議在html
中直接這樣設置一個樣式:
html { scroll-behavior:smooth;}
口說無憑,來看個效果對比,你會有更好的感覺:
那麼回到最初的示例,使用這個屬性就可以搞定了,最終效果如下:
Example: scroll-behavior: smooth這樣的體驗是不是好多了。不過瀏覽器對其支持度相對而言還是一般般:
雖然支持度不是很完美,但如果你想在項目中使用也是無仿的,對於不支持的瀏覽器,可以採用該屬性的polyfill。
有關於scroll-behavior
屬性更多的介紹可以再花點時間閱讀下面這些文章:
- CSSOM View Module:scroll-behavior
- CSS-Tricks: scroll-behavior
- Native Smooth Scroll behavior
- PAGE SCROLLING IN VANILLA JAVASCRIPT
- smooth scroll behavior polyfill
優化滾動邊界
上圖這樣的效果,對於我們而言再正常不過了。當用戶滾動到(Modal彈框或下拉列表)末尾(後再繼續滾動時),整個頁面都會開始滾動。而我們期望的效果是:
滾動到底部時,滾動停止,因為我們到達了「滾動邊界(Scroll Boundary)」。但事實上,在Web頁面中滾動並不會停止,而繼續滾動抽屜(彈框或下拉列表)後面的內容(頁面的內容)。
對於這種效果,被定義為滾動鏈接(Scroll Chaining)。當滾動內容時是瀏覽器的一種默認行為。通常情況下,這種默認情況很好,但有時也不可取,甚至出乎意料。某些應用程序可能希望用戶在遇到滾動邊界時提供不同的用戶體驗。
當滾動元素到達底部時,可以通過(改變)頁面的overflow
屬性或在滾動元素的滾動事件處理函數中取消默認行為來解決這問題。如果你選擇使用JavaScript來處理,要記住,要處理的不是scroll
事件,而是每當用戶使用滑鼠滾輪或觸摸板時觸發的ef="https://developer.mozilla.org/en-US/docs/Web/Events/wheel">wheel事件:
function handleOverscroll(event) { const delta = -event.deltaY; if (delta < 0 && elem.offsetHeight - delta > elem.scrollHeight - elem.scrollTop) { elem.scrollTop = elem.scrollHeight; event.preventDefault(); return false; } if (delta > elem.scrollTop) { elem.scrollTop = 0; event.preventDefault(); return false; } return true; }
不幸的是,這個解決方案不太可靠。同時可能對頁面性能產生負面影響。值得慶幸的是,CSS通過overscroll-behavior
這個新屬性可以幫助我們解決這個問題,達到優化滾動邊界的現象,達到我們想要給用戶的新體驗。
overscroll-behavior
屬性可以控制一個容器或頁面body
容器滾動時發生的默認行為。可以使用這個屬性取消滾動鏈接、禁用、自定義下拉刷新,禁用在iOS上的回彈效果等。而且使用overscroll-behavior
不會像前面提到其他方案一樣對頁面有性能影響。
overscroll-behavior
屬性有三個值:
auto
:其默認值。元素(容器)的滾動會傳播給其祖先元素。有點類似JavaScript中的冒泡行為一樣contain
:阻止滾動鏈接。滾動行為不會傳播給其祖先元素,但會影響節點內的局部顯示。例如,Android上的光輝效果或iOS上的回彈效果。當用戶觸摸滾動邊界時會通知用戶。注意,overscroll-behavior:contain
在html
元素上使用,可以阻止導航滾動操作none
:和contain一樣,但它也可以防止節點本身的滾動效果
overscroll-behavior
屬性是overscroll-behavior-x
和overscroll-behavior-y
的簡寫,如果你只想控制其中一個方向的滾動行為,可以使用其中的某一個屬性。
overscroll-behavior
可以幫助我們:
- 阻止固定元素的滾動傳播
- f="https://github.com/ebidel/demos/blob/master/chatbox.html">禁用下拉刷新pull-to-refresh
- 禁用炫光和回彈效果
overscroll-behavior
也是一個新的CSS特性,到目前為止,支持的瀏覽器還有限:
同樣的,@DylanPiercey也給overscroll-behavior
提供了一個Polyfill。
如果你想更深入的了解overscroll-behavior
屬性,建議你花一些時間閱讀下面相關教程:
- CSS Overscroll Behavior Module Level 1
- ref="https://www.w3cplus.com/css/overscroll-behavior.html">CSS overscroll-behavior
- Take control of your scroll: customizing pull-to-refresh and overflow effects
- How to train a your Scroll
提供一個流式精確的滾動體驗
幻燈片效果,比如Swiper的效果。比如:
對於這樣的效果,我們都想為觸控以及輸入設備的用戶提供一個流式、精確的滾動體驗,比如像下面這樣的效果,讓Card精確定位在正中間:
對於這樣的效果,被稱為Scroll Snap效果,實現類似的效果,有許多JavaScript庫可供你選擇。但我們今天不是來介紹怎麼通過JavaScript庫實現精確的滾動體驗,而是想告訴大家,我們可以使用CSS Scroll Snap Points來實現。
CSS Scroll Snap Points工作原理很簡單:
通過在
x
以及y
軸上定義「snap points」來控制滾動容器的滾動行為。當用戶在水平或者垂直方向滾動時,利用捕捉點,滾動容器會捕捉到內容區域的某一點。
Scroll Snap Points主要提供了以下幾個屬性:
scroll-snap-type
:定義在滾動容器中的一個snap點如何被嚴格的執行scroll-snap-type-x
:定義在滾動容器中水平軸上snap點如何被嚴格的執行scroll-snap-type-y
:定義在滾動容器中垂直軸上snap點如何被嚴格的執行scroll-snap-coordinate
:結合元素的最近的祖先元素滾動容器的scroll-snap-destination
定義的軸,定義了元素中x
和y
坐標偏移的位置。如果元素已經變型,snap坐標也以相同的方式進行變型,為了使元素的snap點向元素一樣被顯示。scroll-snap-destination
:定義滾動容器的可視化viewport 中元素snap點的x
和y
坐標位置scroll-snap-points-x
:定義滾動容器中內容的snap點的水平位置scroll-snap-points-y
:定義滾動容器中內容的snap點的垂直位置scroll-snap-align
:元素相對於其父元素滾動容器的對齊方式。它具有兩個值,x 和 y。如果你只使用其中的一個,另外一個值默認相同scroll-snap-padding
:與視覺窗口的滾動容器有關。工作原理也相近與正常的內邊距,值設置一致。此屬性具有動畫效果,所以如果你想要對齊snap點進行滾動,這將是一個很好的而選擇。
有關於這方在更詳細的介紹建議閱讀下面這些文章:
- 提前思考CSS Scroll Snap Points
- CSS Scroll Snap Points簡介
- CSS Scroll Snap Points規範
- Well-Controlled Scrolling with CSS Scroll Snap
- CSS Scroll Snap: What Is It? Do We Need It?
- Intuitive Scrolling Interfaces with CSS Scroll Snap Points
- Swipe Views with CSS Snap Points: Building a More Efficient Mobile Web Navigation
- Get up to speed with scroll snap points
其他特性
最後再簡單說兩個CSS特性,其中之一就是-webkit-overflow-scrolling: touch;
,另一個即pointer-events:none
。前者主要用來解決慣性滾動的問題,後者主要提高頁面滾動時候的繪製性能。有關於這兩個特性這裡不做過多展開,感興趣的同學可以自己花點時間深糾。
總結
非常感謝你花很長時間把這篇文章閱讀完。整理完之後,感覺有很多內容和@evilmartians的《滾動的特性》一文有點類似。但這篇文章還是圍繞CSS提供的一些新特性:scroll-behavior
、overscroll-behavior
、CSS Scroll Snap Points、::-webkit-scrollbar
、overflow-scrolling: touch
和pointer-events:none
等,為用戶提供一個更好的滾動體驗。如果你感興趣,歡迎花點時間深入了解這些內容。如果你有更好的建議或經驗,歡迎在下面的評論中與我一起共享。
如需轉載,煩請註明出處:https://www.w3cplus.com/css/new-scroll-features-that-change-the-user-experience.html
推薦閱讀:
※遵循這7個原則,能讓你的網頁用戶體驗更優秀
※體驗設計中的 affordance
※現代城市設施建設要將用戶體驗放在重要位置
※About Face 4 第二章(2)
※OkCupid用戶體驗分享