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

JS页面滚动卡顿/抖动问题:求实现稳定平滑文本滚动的方案

平滑滚动文本的卡顿解决思路

问题背景

需要实现速度稳定、无跳帧的平滑滚动文本,支持用户调整速度,但实际遇到以下问题:

  • 使用setInterval:低速时表现流畅,增大像素步长后,即使将间隔设为1ms,滚动仍会变得卡顿,文本难以阅读。
  • 使用requestAnimationFrame:大部分场景下表现完美,但偶尔会出现丢帧,导致页面卡顿数毫秒;短时间内多次丢帧时,卡顿情况会进一步加剧。

当前使用的requestAnimationFrame实现代码:

let start
let scroll_speed = 10
let direction = 1

function scroll(timestamp) {
    if (!start) {
        start = timestamp
    }

    // 检测60Hz屏幕下的帧延迟
    if (timestamp - start >= 17) {
        console.log(timestamp - start)
    }

    start = timestamp

    window.scrollTo({
        top: window.scrollY + (scroll_speed * direction)
    })

    window.requestAnimationFrame(scroll)
}

卡顿发生时,控制台输出的帧延迟示例:

33.459000000031665
32.88800000003539
33.3080000000773

相关技术分析显示:浏览器的渲染帧间隔并非绝对稳定,当主线程被其他任务占用时,requestAnimationFrame会出现丢帧,直接导致滚动速度波动,产生卡顿感。

解决思路

1. 基于时间差计算滚动距离,保证速度稳定

当前代码每帧固定滚动scroll_speed像素,当帧间隔变长(比如从17ms变为33ms),实际每秒滚动的像素数会减半,导致视觉上的跳帧。改为基于帧间隔时间动态计算滚动距离,确保整体速度恒定:

let lastTimestamp = 0
let scrollSpeedPerSecond = 100 // 每秒滚动100像素,可由用户调整
let direction = 1

function scroll(timestamp) {
    const deltaTime = timestamp - lastTimestamp
    lastTimestamp = timestamp

    // 计算该帧应滚动的距离:每秒速度 × 帧间隔(秒)
    const scrollDistance = (scrollSpeedPerSecond / 1000) * deltaTime * direction
    window.scrollBy(0, scrollDistance)

    window.requestAnimationFrame(scroll)
}

核心逻辑是用deltaTime(前后两帧的时间差)动态调整每帧滚动的像素数,即使出现帧延迟,整体滚动速度也能保持一致,消除跳帧感。

2. 用scrollBy替代scrollTo,降低性能开销

scrollTo需要每次计算绝对滚动位置,而scrollBy是基于当前位置进行相对滚动,浏览器对其优化更充分,能减少主线程的布局计算压力,降低丢帧概率。

3. 减轻主线程负载

  • 移除滚动回调中的非必要操作:比如当前代码中的console.log,在卡顿频繁时会加重主线程负担,建议仅在调试时保留。
  • 滚动期间暂停非必要任务:页面上的其他动画、数据请求等任务会抢占主线程资源,可在滚动开始时暂停这些任务,滚动停止后再恢复。

4. 用CSS硬件加速实现滚动(适合简单场景)

如果不需要复杂的实时速度调整逻辑,可采用CSS动画实现自动滚动,浏览器会进行硬件加速,性能更稳定:

/* 滚动容器设置 */
.scroll-container {
    height: 100vh;
    overflow: hidden;
}

/* 滚动内容的动画 */
.scroll-content {
    animation: autoScroll 60s linear infinite;
}

@keyframes autoScroll {
    0% {
        transform: translateY(0);
    }
    100% {
        transform: translateY(-100%);
    }
}

这种方式完全由浏览器处理渲染,避免了JS主线程阻塞导致的卡顿,速度均匀稳定。

5. 防抖处理用户的速度调整操作

当用户通过交互调整滚动速度时,使用防抖函数避免短时间内多次修改速度参数,防止滚动速度突变和额外的计算开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:55:30