频繁调用小步长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
相关产品推荐
相关产品推荐

