掌控滚动操作 - 自定义下拉刷新和溢出效果

要点

借助 CSS overscroll-behavior 属性,开发者可以在到达内容顶部/底部时替换浏览器的默认溢出滚动行为。用例包括在移动设备上停用“拉动即可刷新”功能、移除滚动超出范围时的光晕和橡皮筋效果,以及在页面内容位于模态窗口/叠加层下时阻止页面内容滚动。

背景

滚动边界和滚动链接

Chrome Android 上的滚动链接。

滚动是与网页互动最基本的方式之一,但由于浏览器的默认行为古怪,因此某些用户体验模式可能很难处理。例如,假设有一个包含大量项目且用户可能需要滚动浏览的抽屉式应用列表。当用户滚动到底部时,由于没有更多内容可供使用,因此溢出容器会停止滚动。换句话说,用户到达了“滚动边界”。但请注意,如果用户继续 滚动。抽屉式导航栏后面的内容开始滚动!滚动为 由父级容器接管主页面本身

事实证明,这种行为称为滚动链接,是浏览器滚动内容时的默认行为。默认值通常非常不错,但有时并不理想,甚至出乎意料。某些应用可能需要 可在用户到达滚动边界时提供不同的用户体验。

拉动刷新效果

下拉刷新是一种广为流行的直观手势,例如 Facebook 和 Twitter。向下拉动社交媒体 Feed 并松开手指,系统会创建新的空间来加载较新的帖子。事实上,这种特定的用户体验 变得如此流行,以至于 Android 版 Chrome 等移动浏览器 效果一样在页面顶部向下滑动可刷新整个页面:

<ph type="x-smartling-placeholder">
</ph>
Twitter 的自定义下拉刷新选项
PWA。
<ph type="x-smartling-placeholder">
</ph>
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;
}

通过这项简单的添加,我们修复了聊天框演示中的双重下拉刷新动画,并改为实现使用更简洁的加载动画的自定义效果。收件箱刷新时,整个收件箱也会模糊处理:

之前
之后

以下是完整代码的代码段:

<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;
}
之前:到达滚动边界时会显示一个光晕。
<ph type="x-smartling-placeholder">
</ph>
之后:停用发光效果。

完整演示

综上所述, chatbox demo overscroll-behavior,用于创建自定义下拉刷新动画 以及停用滚动逃离聊天框 widget 的功能。这提供了最优 如果没有 CSS 将难以实现的用户体验 overscroll-behavior

<ph type="x-smartling-placeholder">
</ph>
观看演示 | 来源