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

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;
}

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

頁面重疊顯示情境

「下層捲動」情況的另一個變化版本,是當內容捲動顯示在固定位置重疊後方時。請提供一個絕佳的贈品 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,要達到這個效果相當困難。

查看示範 | 來源