纯 CSS 跑马灯实现无缝滚动必须复制内容并用 translateX + 百分比位移;起点 100%、终点 -100%,配合 linear 时序、nowrap 和硬件加速,动态内容则需 JS 介入。

纯 CSS 跑马灯能实现真正无缝、无 JS、不卡顿的滚动,但必须满足两个硬性条件:内容要复制、位移要用 translateX + 百分比。漏掉任一,就会在循环点出现跳帧或停顿。
为什么必须复制内容?
单段文字从 translateX(100%) 滚到 translateX(-100%) 时,末尾离开视口后,开头还没进视口——中间有空白期。复制一份内容拼接,就能让第二份紧接第一份尾部,视觉上连续。
- HTML 中至少写两遍文本,或用伪元素
::after复制(但动态内容不适用) - 不能靠 JS 插入副本;CSS 方案必须静态可计算
- 若用 Flex 布局实现多子项循环,
width: max-content和transform: translateX(-50%)是关键组合
@keyframes 的起止值怎么设才不闪?
动画起点必须是内容完全在容器右侧外,终点是内容完全在左侧外——且两者位移总量要等于「一个完整内容宽度」。用百分比最稳妥,避免像素值随字体缩放失效。
- 推荐写法:
0% { transform: translateX(100%); } 100% { transform: translateX(-100%); } - 错误写法:
0% { transform: translateX(300px); } 100% { transform: translateX(-300px); }(容器变宽就断层) - 如果内容是双份拼接,实际滚动距离是单份宽度,所以终点仍用
-100%,不是-200%
如何控制速度又保持匀速?
速度由 animation-duration 决定,但它的单位是「滚动一整圈的时间」,不是「每秒多少像素」。内容越长,视觉速度越慢;内容越短,看着越快。想固定视觉流速,得反向算时间。
立即学习“前端免费学习笔记(深入)”;
- 假设希望视觉滚动速度 ≈ 80px/s,内容总宽度为
scrollWidth(可用 DevTools 查看),则设animation-duration: calc(scrollWidth / 80)s - 实际项目中建议用 CSS 自定义属性 +
style动态注入,纯 CSS 无法读取scrollWidth - 务必设
animation-timing-function: linear,否则缓动函数会让首尾变速,破坏无缝感
移动端和响应式容易忽略的坑
跑马灯在小屏上最常出问题:文字被截断、动画突然加速、hover 暂停失效。根本原因是视口变化后,百分比基准变了,但动画没重算。
-
white-space: nowrap必须加在内容元素上,不是容器上;否则换行会撑高容器,溢出隐藏失效 - 用
will-change: transform触发硬件加速,尤其在 iOS Safari 上能明显减少卡顿 - 暂停交互只靠
:hover不够——手机没 hover;需配合@media (hover: none)降级为常动,或加 JS 监听touchstart
最麻烦的其实是动态内容:Vue/React 渲染后文本长度变了,但 CSS 动画 duration 还是旧的。这时候纯 CSS 就到头了,得切到 requestAnimationFrame + 宽度监听方案。别硬撑。


















