根本原因是requestAnimationFrame频率和CSS动画时长单位在不同硬件上表现不一致;应使用时间戳驱动位移(位移量=速度×Δt/1000),配合linear缓动、禁用will-change、用px/vw单位,并监听resize/orientationchange防抖重算。

滚动文字在不同设备上速度不一致的根本原因
核心问题不是“文字动得快慢”,而是 requestAnimationFrame 的执行频率和 CSS 动画的时长单位(如 s、ms)在不同硬件上表现不一致。高刷屏(120Hz)下,requestAnimationFrame 每秒触发约 120 次;老旧手机可能只有 30–45fps,同一段基于帧数的位移逻辑(比如“每帧移动 2px”)就会明显变慢。
更隐蔽的是:CSS 中用 animation-duration: 3s 驱动的滚动,在低性能设备上可能因渲染丢帧,导致视觉上“卡顿+变速”,并非匀速滚动。
用 transform + 时间戳替代帧数位移
避免写类似 element.style.transform = 'translateX(' + (pos += 2) + 'px)' 这种依赖固定增量的逻辑。应改用时间差驱动:
- 记录上一帧时间戳(
lastTime) - 每次
requestAnimationFrame触发时,计算与上次的时间差delta - 位移量 = 期望速度(px/s) ×
delta / 1000
let lastTime = 0;
const speed = 60; // px per second
function scrollLoop(timestamp) {
if (!lastTime) lastTime = timestamp;
const delta = timestamp - lastTime;
lastTime = timestamp;
const move = speed * delta / 1000;
el.style.transform = translateX(${currentX -= move}px);
requestAnimationFrame(scrollLoop);
}
这样无论设备是 30fps 还是 120fps,单位时间内总位移量都严格一致。
立即学习“前端免费学习笔记(深入)”;
CSS 滚动动画必须用 linear 且禁用 will-change 意外开销
如果坚持用 CSS 动画(比如 @keyframes scroll),注意三点:
- 动画函数必须设为
animation-timing-function: linear,否则默认cubic-bezier(.25,.1,.25,1)会引入非线性加速,放大设备间差异 - 不要对滚动容器加
will-change: transform,尤其在低端 Android 上,它可能触发不必要的图层合成,反而拖慢帧率 - 避免用
%做位移单位(如translateX(-100%)),因为父容器宽度假如受字体缩放或 viewport 变化影响,实际像素值会漂移;优先用px或vw等绝对/视口单位
移动端需监听 resize 和 orientationchange 重算滚动参数
平板横竖屏切换、iOS Safari 地址栏显隐、Android 软键盘弹出都会改变 window.innerHeight 和可用滚动区域。若滚动逻辑依赖容器高度(比如无限滚动翻页临界值),这些变化会导致:
- 滚动提前触发加载
- 或永远触不到临界点,数据不刷新
应对方式:
- 把关键尺寸(如容器高度、滚动阈值像素值)封装成函数,每次
scroll前调用 - 对
resize和orientationchange做防抖(setTimeout延迟 100ms),避免频繁重算
真正难的不是让文字动起来,而是让它在红米 Note 9 和 iPhone 15 Pro 上,同一秒内走过完全相同的像素距离——这要求你放弃“帧数思维”,只信任时间戳和物理单位。











