JavaScript自动滚动功能在iOS端Safari上出现抖动卡顿问题
实现目标
为在线电子书阅读器网站开发自动滚动功能,支持用户点击对应按钮后触发页面自动滚动效果。
问题描述
目前主流公开的自动滚动实现方案,均通过setTimeout或setInterval定时器调用window.scrollBy(0, 1)实现滚动逻辑,该方案在Chrome浏览器中运行流畅无异常,但在iOS端Safari浏览器测试时,页面滚动流畅度极差,存在非常明显的抖动现象,严重时滚动过程中的文字完全无法清晰辨认。
问题演示视频建议选择1080p60清晰度观看,60fps帧率下抖动现象更易观察
复现代码
下方是当前所用自动滚动实现逻辑的简易HTML演示文档:
JavaScript逻辑
let scrollTimeout; function pageScroll() { scrollTimeout = setTimeout(() => { window.scrollBy(0, 1); pageScroll(); }, 10); } function startScroll() { pageScroll(); } function stopScroll() { clearTimeout(scrollTimeout); }
HTML结构
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Test Auto-scroll</title> </head> <body> <main style="max-width: 500px; margin: 0 auto; font-size: 32px"> <div style="position: sticky; top: 0"> <button onclick="startScroll()">Start</button> <button onclick="stopScroll()">Stop</button> </div> <p> Lorem ipsum dolor sit amet consectetur adipisicing elit. Earum ducimus, totam debitis sint modi inventore explicabo iusto assumenda vitae vero porro aperiam aut reiciendis nam culpa quia in, dolor quae. </p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Impedit sunt vitae saepe recusandae voluptatibus nihil enim, sequi rerum quibusdam nisi provident! Autem aliquid reprehenderit doloremque inventore quasi dolore fugit tempora. </p> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Iure molestias consectetur molestiae labore totam ex et eum nulla officia, minima excepturi voluptates, consequatur amet modi! Molestiae autem illum ullam accusantium. </p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam ab ex tenetur a possimus? Cum, nostrum dolor? Veniam tenetur cupiditate, maxime fuga sequi earum excepturi repudiandae, aliquam, laborum blanditiis iusto. </p> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ipsam, laborum mollitia suscipit dolores omnis, cum perferendis rerum accusantium doloremque quasi pariatur, deserunt delectus et porro nesciunt cumque vel quisquam velit! </p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam ab ex tenetur a possimus? Cum, nostrum dolor? Veniam tenetur cupiditate, maxime fuga sequi earum excepturi repudiandae, aliquam, laborum blanditiis iusto. </p> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ipsam, laborum mollitia suscipit dolores omnis, cum perferendis rerum accusantium doloremque quasi pariatur, deserunt delectus et porro nesciunt cumque vel quisquam velit! </p> <p> Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quisquam ab ex tenetur a possimus? Cum, nostrum dolor? Veniam tenetur cupiditate, maxime fuga sequi earum excepturi repudiandae, aliquam, laborum blanditiis iusto. </p> <p> Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ipsam, laborum mollitia suscipit dolores omnis, cum perferendis rerum accusantium doloremque quasi pariatur, deserunt delectus et porro nesciunt cumque vel quisquam velit! </p> </main> </body> </html>
故障原因
setTimeout/setInterval的执行时机由JS事件循环调度,和浏览器原生渲染帧率完全不同步,iOS Safari对非渲染线程调度触发的滚动操作有额外性能开销,每次1px的高频滚动会触发频繁重排重绘,主线程一旦出现阻塞就会产生明显抖动。- iOS Safari对定时器触发的滚动有事件节流机制,滚动位移无法对齐设备原生刷新率(60/120Hz),视觉上就会出现跳帧、卡顿感。
修复方案
使用requestAnimationFrame替代定时器实现滚动逻辑,该API由浏览器渲染线程统一调度,执行时机和屏幕刷新率完全对齐,在iOS Safari上可以获得和原生滚动一致的流畅度。
优化后的核心代码:
let scrollRafId; // 可根据需求调整滚动速度,数值越大滚动速度越快 const scrollSpeed = 1; function pageScroll() { window.scrollBy(0, scrollSpeed); scrollRafId = requestAnimationFrame(pageScroll); } function startScroll() { // 避免重复调用创建多个滚动任务 if (scrollRafId) return; scrollRafId = requestAnimationFrame(pageScroll); } function stopScroll() { cancelAnimationFrame(scrollRafId); scrollRafId = null; }
额外优化建议
- 给页面根元素添加
-webkit-overflow-scrolling: touchCSS属性,启用iOS端硬件加速的惯性滚动能力 - 自动滚动过程中避免执行触发DOM重排的操作,减少主线程性能占用
- 可根据用户设备刷新率动态调整scrollSpeed参数,在高刷屏上获得更匹配的滚动速度
内容的提问来源于stack exchange,提问作者Saief El Gebali
相关产品推荐
相关产品推荐

