如果我使用-webkit-overflow-scrolling,DIV滚动有时会冻结

66

如果我在一个滚动的div上使用-webkit-overflow-scrolling属性,它会以本地动量完美地滚动。但是,这个div本身有时候会冻结并且不响应我的手指移动。2-3秒钟后,它又变得可以滚动。

我不知道我如何复现这个问题。但是,我发现有两种主要行为会导致这种情况。

第一,如果我等待一段时间,例如20秒,然后触摸这个div,它就不会响应。我等几秒钟后,它又开始工作了。

第二,我快速连续地点击几次,然后它就会冻结,然后再过几秒钟后,它又重新开始工作了。

我该如何防止这种冻结问题?


2
你可以尝试强制使用硬件加速吗?例如,如果你的滚动div的类名为element,在你的CSS中使用.element > * { -webkit-transform: translateZ(0px); } - Gokhan Kurt
6
您可以展示您的代码吗?或者提供存在问题页面的链接? - Narek-T
请在帖子中指定您的浏览器及其版本,谢谢! - Ahmed I. Elsayed
我使用iPhone 6s。我尝试了@GökhanKurt的代码,但没有帮助。我认为这是一个操作系统的错误,对于具有动态内容的元素。顺便说一下,这在Android设备上不会发生,只会在iOS上发生。我在iPhone上尝试了Safari和Chrome。 - Adem
我现在正好遇到了这个问题。您是否曾经成功解决过这个问题,如果是,您是如何解决的? - Petya Naumova
显示剩余8条评论
7个回答

19

对我来说,冻结是可重复的,并且在尝试向上或向下滚动时已经到达顶部或底部时发生。解决方法是添加一些touchstarttouchmove监听器,并检测这些情况并在它们上面调用event.preventDefault()

类似以下内容,其中.scroller是实际滚动的div(更改scrollTop)。

var lastY = 0;
var targetElt = document.querySelector(".scroller");

targetElt.addEventListener('touchstart', function(event) {
    lastY = event.touches[0].clientY;
});

targetElt.addEventListener('touchmove', function(event) {
    var top = event.touches[0].clientY;

    var scrollTop = event.currentTarget.scrollTop;
    var maxScrollTop = event.currentTarget.scrollHeight -
        $(event.currentTarget).outerHeight();
    var direction = lastY - top < 0 ? 'up' : 'down';

    if (
        event.cancelable && (
            (scrollTop <= 0 && direction === 'up') ||
            (scrollTop >= maxScrollTop && direction === 'down')
        )
    )
      event.preventDefault();

    lastY = top;
});

我希望这能帮助下一个遭遇这个可怕的bug的可怜人!祝你好运,继续战斗!


3
这是最终对我起作用的解决方法!在iOS Safari中打开live example并查看其代码。唯一的缺点是当已经到达顶部/底部时,弹跳效果会有些故障,但这是可以接受的,并且似乎是直到WebKit维护者修复该错误为止我们所拥有的最佳选项。 - Mikhail Vasin
1
@whyAto8 iOS Safari的bug ¯_(ツ)_/¯,可能与它在顶部和底部“弹跳”的方式有关。似乎是因为已经到达了那里,它可能会触发弹跳效果并且在完成之前无法响应,但由于您已经到达那里,它只是出现了错误。 - Wesley Reitzfeld
@WesleyReitzfeld 好的,我明白了。这是他们已经记录了这个问题,但却不想解决它的意思。 - whyAto8
@WesleyReitzfeld 没问题。你的解决方案看起来很好,对我也很有效。谢谢。 - whyAto8
1
对于那些在2021年不使用jQuery的人来说,你可以用el.offsetHeight替换el.outerHeight() - Solo
显示剩余7条评论

10

尝试在 body 上使用 overflow: hidden。这应该解决问题:https://codepen.io/cppleon/pen/vYOgKzX

HTML

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  </head>
  <body>
    <div id="scrollable-content">
      <div class="site-header"></div>
      <div class="main-content"></div>
    </div>
  </body>
</html>

CSS

body {
  /* magic is here */
  overflow: hidden;
}

#scrollable-content {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 50%;
  background-color: gray;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.site-header {
  width: 100%;
  height: 120px;
  background-color: orange;
}

.main-content {
  height: 200%;
}

1
当你同时进行快速滚动和点击底部或顶部时,iPhone 上会经常出现滚动冻结问题,而当滚动完全隐藏时,滚动问题会消失。在body中使用overflow: hidden解决了我的问题。 - victor.chicu

5

稳定的解决方案

经过多天的尝试修复,我发现问题来自于固定的body元素,可能是因为您不希望用户在滚动被阻止时看到页面主体弹跳:参考此示例。 当主体固定且您遇到滚动冻结错误时,如果您在iOS设备上使用桌面Safari检查主体,则可以看到它在“人为地”移动...是的,这是webkit的东西...

我尝试了此威胁中列出的所有解决方案,但也在github类似问题上尝试了。没有一个有效。

对我来说唯一稳定的解决方案是使用此软件包body-scroll-lock删除body元素上的fixed。现在,您既可以享受固定的主体,又可以避免滚动冻结错误。

希望它能帮助正在创建IOS渐进式Web应用程序的人们。


你是如何使用 body-scroll-lock 来防止页面冻结的? - Jette
我将其应用于每个滚动元素。 - Aarbel

3
我使用了以下代码,我认为它是有效的。
var scrollTimer;
$('.scroller').on('scroll',function(e){
      clearTimeout(scrollTimer);
      scrollTimer = setTimeout(() => {
        this.scrollTop = Math.max(1, Math.min(this.scrollTop, this.scrollHeight - this.clientHeight - 1));
      }, 300);
});

1
哇:这对我来说实际上是有效的。知道你是如何做到的会很好。 - Marcin Czenko

1

我遇到了同样的问题。但是这个问题很容易解决。 以下是我所做的: 删除了可滚动的 div 的 height 属性。 也许你的情况和我不同,这个方法对你可能不适用。


0

我知道这已经很老了,但也许还有其他人遇到了同样的问题。对我来说,问题是由 iNoBounce (https://github.com/lazd/iNoBounce) 引起的。Y 轴滚动很好,但 X 轴滚动会引起很多问题,元素会卡住,你必须多次触摸和移动才能最终滚动。

删除 iNoBounce 后,除了明显的滚动反弹(特别是“过度滚动”)外,再也没有任何问题了。为了禁用过度滚动,我使用了以下方法,但现在滚动反弹还在。

html { height: 100%; position: fixed; overflow: hidden; }
body { height: 100%; position: relative; overflow: auto; }

0
最近我遇到了这个bug,尝试了很多hacky的解决方案后,我们发现最好的方法就是如果视图在底部,则将其滚动一个像素。这可以防止“冻结”,我认为实际上是当嵌套容器完全滚动到底部时,body/window接收到滚动事件。这是使用React实现的,但你可以理解这个思路。
const listener = () => {
    window.requestAnimationFrame(() => {
      if (!this.scrollRef.current) {
         return;
      }
   
      const { scrollTop, scrollHeight, clientHeight } = this.scrollRef.current;

      if (scrollTop === scrollHeight - clientHeight) {
          // at the bottom
          this.scrollRef.current.scrollTo(0, scrollHeight - clientHeight - 1);
      }
    });
}

this.scrollRef.current.addEventListener("scroll", listener);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接