掌控捲動方式 - 自訂下拉即可重新整理和溢位效果

TL;DR

CSS overscroll-behavior 屬性可讓開發人員在到達內容頂端/底部時,覆寫瀏覽器的預設溢位捲動行為。用途包括在行動裝置上停用「拉動重新整理」功能、移除捲動超出螢幕邊緣的發光效果和橡皮筋效果,以及在模式視窗/重疊層下方,防止網頁內容捲動。

背景

捲動邊界和捲動鏈結

在 Android 版 Chrome 上連結捲動動作。

捲動是與網頁互動最基本的一種方式,但由於瀏覽器的奇怪預設行為,因此處理某些使用者體驗模式可能會相當棘手。舉例來說,應用程式抽屜中可能包含大量項目,使用者可能需要捲動畫面才能瀏覽。當他們到達底部時,溢位容器會停止捲動,因為沒有可用的內容。換句話說,使用者會到達「捲動邊界」。但請注意,如果使用者繼續捲動畫面,會發生什麼情況。抽屜後方的內容開始捲動!捲動作業會由父項容器接管,也就是範例中的主頁面本身。

原來這種行為稱為捲動鏈結,是瀏覽器在捲動內容時的預設行為。預設值通常都很不錯,但有時可能不符合需求,甚至會造成意外。某些應用程式可能會在使用者觸及捲動邊界時,提供不同的使用者體驗。

滑動重新整理效果

拉動刷新是一種直覺手勢,由 Facebook 和 Twitter 等行動應用程式廣為流行。向下滑動社群動態消息並放開,即可為載入較新的貼文騰出空間。事實上,這類使用者體驗已變得相當受歡迎,Android 版 Chrome 等行動瀏覽器也採用了相同的效果。在頁面頂端向下滑動可重新整理整個頁面:

在 PWA 中重新整理動態時,使用 Twitter 的客製化「拉動重新整理」
Chrome Android 的原生拉動更新動作
會重新整理整個頁面。

在 Twitter PWA 這類情況下,建議您停用原生的下拉重新整理動作。這是因為在這個應用程式中,您可能不希望使用者誤觸重新整理頁面。您也可能會看到雙重重新整理動畫!或者,您也可以自訂瀏覽器的動作,讓動作更貼近網站的品牌形象。不幸的是,這類自訂功能很難實現。開發人員最終會編寫不必要的 JavaScript、新增非被動觸控事件監聽器 (會阻擋捲動),或是將整個網頁貼在 100vw/vh <div> 中 (以免網頁溢位)。這些因應措施對捲動效能有已充分記錄的負面影響。

我們可以做得更好!

隆重推出 overscroll-behavior

overscroll-behavior 屬性是新的 CSS 功能,可控制當您過度捲動容器 (包括頁面本身) 時,系統會採取的行為。您可以使用它取消捲動鏈結、停用/自訂拉動重新整理動作、在 iOS 上停用橡皮筋效果 (當 Safari 實作 overscroll-behavior 時) 等等。最棒的是,使用 overscroll-behavior 不會對網頁效能造成負面影響,這一點與前文提到的駭客攻擊不同!

這個屬性可使用三個值:

  1. auto:預設值。元素產生的捲動動作可能會傳播至祖系元素。
  2. contain:可防止捲動連結。捲動不會傳播至祖系,但節點內的本機效果會顯示。例如,Android 上的超出捲動發光效果,或 iOS 上的橡皮筋效果,可在使用者觸及捲動邊界時通知使用者。注意:在 html 元素上使用 overscroll-behavior: contain 可防止捲動過頭的導覽動作。
  3. none:與 contain 相同,但也會防止節點內的過度捲動效果 (例如 Android 過度捲動發光或 iOS 橡皮筋效應)。

讓我們來看看如何使用 overscroll-behavior 的幾個範例。

防止捲動畫面離開固定位置元素

聊天方塊情境

即時通訊視窗下方的內容也會捲動 :(

建議將即時通訊方塊固定在網頁底部。意圖是讓聊天方塊成為獨立的元件,並且與後方內容分開捲動。不過,由於捲動鏈結,使用者只要點選聊天記錄中的最後一則訊息,文件就會開始捲動。

對於這個應用程式來說,讓起源於聊天框的捲動動作停留在聊天中會比較適當。我們可以將 overscroll-behavior: contain 新增至儲存即時通訊訊息的元素,即可實現這項功能:

#chat .msgs {
  overflow: auto;
  overscroll-behavior: contain;
  height: 300px;
}

基本上,我們會在聊天框捲動內容和主頁面之間建立邏輯區隔。最終結果是,當使用者瀏覽到聊天記錄的頂端/底部時,主頁面會保持不變。從聊天框開始的捲動不會傳播。

頁面重疊顯示情境

「underscroll」情境的另一種變化版本是當您看到內容在固定位置重疊層後方捲動時。請提供一個絕佳的贈品 overscroll-behavior!瀏覽器雖然試圖提供協助,但最終卻讓網站看起來有問題。

範例 - 含有和不含 overscroll-behavior: contain 的模式對話方塊:

修正前:頁面內容會在重疊層下方捲動。
修正後:頁面內容不會在重疊層下方捲動。

停用拖曳重新整理功能

關閉「拉動重新整理」動作只需要一行 CSS。只要避免在整個視區定義元素上鏈結捲動畫面即可。在大多數情況下,這個值是 <html><body>

body {
  /* Disables pull-to-refresh but allows overscroll glow effects. */
  overscroll-behavior-y: contain;
}

透過這項簡單的新增功能,我們修正了 Chatbox 示範中的雙重拉動重新整理動畫,並改為實作使用更簡潔的載入動畫的自訂效果。收件匣重新整理時,整個收件匣也會模糊處理:

變更前
變更後

以下是完整程式碼的程式碼片段:

<style>
  body.refreshing #inbox {
    filter: blur(1px);
    touch-action: none; /* prevent scrolling */
  }
  body.refreshing .refresher {
    transform: translate3d(0,150%,0) scale(1);
    z-index: 1;
  }
  .refresher {
    --refresh-width: 55px;
    pointer-events: none;
    width: var(--refresh-width);
    height: var(--refresh-width);
    border-radius: 50%;
    position: absolute;
    transition: all 300ms cubic-bezier(0,0,0.2,1);
    will-change: transform, opacity;
    ...
  }
</style>

<div class="refresher">
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
  <div class="loading-bar"></div>
</div>

<section id="inbox"><!-- msgs --></section>

<script>
  let _startY;
  const inbox = document.querySelector('#inbox');

  inbox.addEventListener('touchstart', e => {
    _startY = e.touches[0].pageY;
  }, {passive: true});

  inbox.addEventListener('touchmove', e => {
    const y = e.touches[0].pageY;
    // Activate custom pull-to-refresh effects when at the top of the container
    // and user is scrolling up.
    if (document.scrollingElement.scrollTop === 0 && y > _startY &&
        !document.body.classList.contains('refreshing')) {
      // refresh inbox.
    }
  }, {passive: true});
</script>

停用過度捲動發光和橡皮筋效果

如要停用捲動邊界時的彈跳效果,請使用 overscroll-behavior-y: none

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
先前:捲動到邊界時會顯示發光效果。
變更後:已停用餘韻場域。

完整展示模式

將所有內容整合後,完整的聊天框示範會使用 overscroll-behavior 建立自訂的拉動重新整理動畫,並停用捲動畫面以免離開聊天框小工具。這可提供最佳使用者體驗,如果沒有 CSS overscroll-behavior,要達到這個效果相當困難。

查看產品示範 | 來源