无缝滚动需双份内容+200%容器宽+translateX(-100%),配white-space:nowrap和display:block消除inline间隙,hover暂停需JS重置计时器,必加will-change:transform启用硬件加速。

直接用 @keyframes + transform: translateX() 做无限循环滚动,90% 的人第一步就错在“只动一次、硬跳回起点”——这会导致视觉闪动或位移断层。真正无缝的关键不是靠动画终点“跳回去”,而是让容器宽度和动画位移量对齐复制内容的物理长度。
为什么单组图片 100% { transform: translateX(-100%) } 会闪
浏览器渲染有帧间间隙,translateX(-100%) 到达终点后,下一帧若没立刻重置位置(比如靠 0% { transform: translateX(0) }),就会出现短暂空白或重绘撕裂。尤其在低帧率设备或高DPI屏上更明显。
- 错误做法:仅 3 张图,
.track { width: 100%; animation: scroll 8s linear infinite; },动画里从0%到100%移动 -100%,终点和起点不连续 - 正确逻辑:必须让动画结束时,视口刚好对齐“第二组首图”的起始位置,而非原地复位
- 所以 DOM 要放双份图(如原 3 张 → 实际 6 张),
.track { width: 200%; },动画位移量设为-100%(即一张图宽度 × 图数量)
white-space: nowrap 和 display: block 必须同时用
默认 <img> 是 inline 元素,HTML 换行/空格会转成约 4px 的间隙,导致 .track 实际宽度 > 计算值,动画位移错位——你算好移 300px,结果它宽了 12px,最后对不齐。
- 父容器加
white-space: nowrap阻止换行 - 所有
<img>加display: block或vertical-align: top消除基线间隙 - 更稳妥:外层再包一层
<div style="font-size: 0">,彻底干掉 inline 间隙 - 别用
margin: -4px这类 magic number,不同字体、系统渲染下不稳定
鼠标悬停暂停时如何不抽搐
:hover { animation-play-state: paused } 看似简单,但恢复播放时会从当前时间点继续(比如停在 67.3%),而位移是线性累积的,会导致下一帧突然“跳”一段,破坏平滑感。
立即学习“前端免费学习笔记(深入)”;
- 真正要的是:暂停时保持当前
transform值不变,恢复时从该值开始匀速接续 - CSS 本身做不到“冻结 transform 值并重置动画计时器”,所以得靠 trick:
animation-play-state: paused配合animation-duration动态重设(需 JS);纯 CSS 场景下,唯一可靠方式是让动画周期足够长(如 20s+),降低暂停感知 - 如果必须纯 CSS,可改用两段动画:主滚动用长周期,hover 时切到一个极慢的
0s动画占位,但兼容性差、难维护 - 实际项目中,建议 hover 暂停 + 点击切换手动控制,比强求“无缝恢复”更可控
will-change: transform 不是可选项,是必填项
没有硬件加速时,translateX() 滚动在中低端 Android 或 Safari 上极易掉帧、发虚,尤其图片多或容器宽时。
- 必须加
will-change: transform,或退而求其次用transform: translateZ(0) - 注意:不要给整个
.track加,只加在动画元素上(如.track本身) - 别滥用:只对持续动画的元素加,否则触发过多图层合成,反而拖慢页面
- 移动端务必测试
touch-action: none,防止滚动冲突
最易被忽略的点:动画时长不能靠感觉调。比如 5 张图,每张宽 200px,间隙 20px,总宽 1100px,那 animation-duration 应基于人眼舒适滚动速度反推(通常 15–30s 一圈),而不是随便写个 8s——太快让人眼跟不上,太慢像卡顿。


















