Skip to content
文章目录

IOS浏览器禁用橡皮筋效果

橡皮筋效果是指什么?

使用 IOS 端 浏览器打开网页,在滚动条滚动到顶部之后,手指继续向下滑动,页面依旧会被下拉,且被强制下拉出来的部分是空白的,且下拉到一定程度会产生页面刷新

哪些地方会出现橡皮筋效果

在 IOS 端凡属滚动容器都会出现橡皮筋效果,不过 body 的橡皮筋效果,除了橡皮筋的回弹效果外,如果继续下拉到一定程度会产生页面刷新。而其他非 body 的滚动容器的橡皮筋效果,只会有下拉回弹,并不会产生页面刷新

如何触发橡皮筋效果

必须同时满足以下两个条件

  1. 必须是滚动容器
  2. 下拉到顶部之后继续下拉

如何禁用橡皮筋效果?

如果不考虑兼容性问题,css 大法即可解决:

css
body {
  /* 禁用默认的下拉刷新和边界效果,但是依然可以进行滑动导航 */
  overscroll-behavior-y: none;
}

can i use: overscroll-behavior-y

既然是滚动容器就会有橡皮筋效果,那么只要容器不是滚动容器就不会触发橡皮筋效果了。

是否感觉上面这段话是无意义的?非也。

禁用 body 的橡皮筋效果

直接将 body 设置为非滚动容器, 此时 body 就没有橡皮筋效果了。但 body 的子元素如果包含滚动容器,那么那个滚动容器的橡皮筋效果依然存在

css
body {
  overflow: hidden;
}

禁用非 body 的橡皮筋效果

知道了橡皮筋效果的触发条件,那我们使条件不满足不就可以了。

方式一: 通过阻止默认行为,来禁止滚动,从而达到禁用橡皮筋效果的目的

缺陷:

  1. 较为复杂
  2. 当滚动条初始停留在接近顶部的地方。如: scrollTop 为 5px 的位置,此时往下滑动很长一段距离,还是会有橡皮筋效果

WARNING

方式一有一点点缺陷

ts
/**
 * 判断是否滚动到了最底部
 */
function isScrolledBottom(scrollHeight: number, clientHeight: number, scrollTop: number) {
  return Math.abs(scrollHeight - clientHeight - scrollTop) < 1
}
/**
 * 判断滚动容器是否滚动到了最底部
 */
function validateElementIsScrolledBottom(element: HTMLElement) {
  const { scrollHeight, clientHeight, scrollTop } = element
  return isScrolledBottom(scrollHeight, clientHeight, scrollTop)
}
interface TouchEventXPJ extends TouchEvent {
  /**
   * 是否应该支持滚动
   */
  isNeedScroll: boolean
}
function disableEffectXPJ(needScrollDom: HTMLElement) {
  //   needScrollDom.addEventListener('touchmove', function (e: TouchEvent) {
  //     const evt = e as TouchEventXPJ
  //     evt.isNeedScroll = true
  //   })
  let startY = 0
  document.body.addEventListener(
    'touchstart',
    e => {
      startY = e.touches[0].pageY
    },
    { passive: false }
  )
  document.body.addEventListener(
    'touchmove',
    function (evt: TouchEvent) {
      const e = evt as TouchEventXPJ
      if (!e.isNeedScroll) {
        // 无需支持滚动,则通过阻止默认行为,达到阻止滚动的目的
        e.preventDefault()
      } else {
        //需要滑动的区域
        const { touches } = e
        const moveY = touches[0].pageY
        const { scrollTop } = el
        /**
         * 手指是否在向下滑动
         */
        const isPositive = moveY > startY
        /**
         * 滚动条是否滚动到了最底部
         */
        const isScrolledEnd = validateElementIsScrolledBottom(el)
        if ((scrollTop <= 0 && isPositive) || (isScrolledEnd && !isPositive)) {
          // 已经滚动到了顶部手指还在向下滑动,或者已经滚动到了底部手指还在向上滑动,则阻止默认事件
          e.preventDefault()
        }
      }
    },
    { passive: false }
  ) //passive防止阻止默认事件不生效
}

方式二:将滚动容器在允许滚动和禁止滚动之间动态切换

ts
function onScroll(evt: UIEvent) {
  const scrollDom = evt.target as HTMLElement
  if (!scrollDom) return

  const { scrollTop } = scrollDom
  if (scrollTop <= 0) {
    // 已经滑动到了顶部,则将容器变为非滚动容器,用于避免出现橡皮筋效果
    scrollDom.style.overflow = 'hidden'
    setTimeout(() => {
      // 再将容器恢复为允许滚动
      scrollDom.style.overflow = 'auto'
    })
  }
}

其他补充

IOS 的 safari 滚动卡顿?

试试将如下 css 添加到滚动容器中

css
.scrollDom {
  /* 该属性用于解决IOS滚动卡顿, 
  如果滚动容器的层级之上有漂浮的绝对定位元素,
  那么需要对绝对定位元素设置z-index值,
  否则可能会导致滚动容器的内容显示再绝对定位元素的上层 */
  -webkit-overflow-scrolling: touch;
}

-webkit-overflow-scrolling :控制元素在移动设备上是否使用滚动回弹效果。 auto :使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止。 touch :使用具有回弹效果的滚动, 当手指从触摸屏上移开,内容会继续保持一段时间的滚动效果,继续滚动的速度和持续的时间和滚动手势的强烈程度成正比。

需要注意的事项:

通过动态添加内容撑开容器,结果根本不能滑动。 在 safari 上,点击其他区域,再在滚动区域滑动,滚动条无法滚动。  在 safari 上,使用了-webkit-overflow-scrolling:touch 之后,页面偶尔会卡住不动。

解决问题的方法:

第一步:将使用-webkit-overflow-scrolling
        的元素的定位取消或手动改成 position:static;

第二步:将使用-webkit-overflow-scrolling 的元素添加一个子元素,
        设置子元素的高度为 height:101%,或者为了精确一些写成
        height:calc(100vh + 1px), 注意这个加号前后都要有空格.

移动端页面在 IOS 里滑动不顺畅解决办法

参考资料

vue 结合 preventDefault()和页面滚动高度计算,解决 ios 橡皮筋效果

H5 下拉刷新(解决与 android 微信端下拉冲突、与页面滚动冲突)

h5 ios 滑动白屏(h5 滑动和 webview 滑动动作冲突)

阻止移动端浏览器下拉橡皮筋效果(下拉滚动露底)

h5 下拉刷新实现

h5 下拉刷新-pullRefresh v1.1

解决 IOS 13+ H5 滑动边界橡皮筋弹性效果

面试官:如何实现上拉加载,下拉刷新?

解决 IOS 橡皮筋效果

处理 iOS 微信 H5 页面橡皮回弹效果

如何禁止 IOS 的橡皮筋效果