You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

频繁调用小步长window.scrollBy()导致移动端滚动抖动问题问询

iOS ProMotion屏幕下频繁小步长调用window.scrollBy导致滚动抖动问题解析与解决方案

问题现象

在iPhone 13 Max Pro(推测为iOS ProMotion高刷新率屏幕专属问题)上,频繁以小步长调用window.scrollBy(...)会出现严重的滚动抖动;无ProMotion屏幕的MacBook Pro则无此问题。

复现代码

极简复现代码如下(需本地HTTP服务部署,手机端访问,iframe沙箱环境无法复现):

<html>
  <body>
    <div id="app" />
    <script>
      function createDivs() {
        const appDiv = document.getElementById("app");
        for (let i = 0; i < 1000; i++) {
          const div = document.createElement("div");
          div.textContent = "Test";
          div.style.fontSize = "44px";
          appDiv.appendChild(div);
        }
      }

      function App() {
        createDivs();
        setInterval(() => {
          window.scrollBy(0, 5);
        }, 10);
      }

      App();
    </script>
  </body>
</html>

临时解决方案原理

将滚动上下文从根元素(html)转移到body元素后抖动消失,核心原因:

  • iOS Safari对根滚动上下文(即window/html的滚动)有系统级特殊优化,比如弹性滚动、滚动动量计算等。在ProMotion 120Hz高刷新率下,这些优化逻辑和setInterval(10ms间隔对应100Hz)的帧率不匹配,导致JS触发的滚动与系统滚动处理冲突,产生抖动。
  • 当滚动上下文切换到body元素时,属于普通DOM元素滚动,系统不会应用根滚动的特殊优化,JS触发的滚动能精准按照设定步长和间隔执行,避免了高刷新率下的帧率冲突。

修改后的代码:

<html>
  <head>
    <style>
      html {
        overflow-y: hidden;
      }
      body {
        overflow-y: auto;
        height: 100dvh;
      }
    </style>
  </head>
  <body>
    <div id="app" />
    <script>
      function createDivs() {
        const appDiv = document.getElementById("app");
        for (let i = 0; i < 1000; i++) {
          const div = document.createElement("div");
          div.textContent = "Test";
          div.style.fontSize = "44px";
          appDiv.appendChild(div);
        }
      }

      function App() {
        createDivs();
        setInterval(() => {
          document.body.scrollBy(0, 5);
        }, 10);
      }

      App();
    </script>
  </body>
</html>

更优解决方案(无需切换滚动上下文)

1. 使用requestAnimationFrame替代setInterval

让滚动触发与浏览器渲染帧同步,完美适配ProMotion高刷新率,从根源避免帧率不匹配问题:

function App() {
  createDivs();
  function scrollStep() {
    window.scrollBy(0, 5);
    requestAnimationFrame(scrollStep);
  }
  requestAnimationFrame(scrollStep);
}

requestAnimationFrame会在浏览器每一次重绘前执行回调,确保滚动操作和屏幕刷新率同步,彻底消除抖动。

2. 全局封装兼容滚动方法(无需团队修改原有调用逻辑)

如果团队仍希望保留window.scrollBy的调用形式,可以封装自动适配方法,无需修改原有代码:

// 全局替换原生scrollBy,自动适配场景
const originalScrollBy = window.scrollBy;
window.scrollBy = function(x, y) {
  // 检测iOS ProMotion屏幕场景(可根据实际情况调整判断逻辑)
  const isProMotionIOS = navigator.userAgent.includes("iPhone") && window.devicePixelRatio >= 3;
  if (isProMotionIOS && document.body.scrollHeight > document.body.clientHeight) {
    document.body.scrollBy(x, y);
  } else {
    originalScrollBy.call(window, x, y);
  }
};

这样团队无需改动原有业务代码,即可自动适配iOS高刷屏场景。

内容的提问来源于stack exchange,提问作者user1877760

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 17:27:42