直接用@keyframes设translateX(-100%)会导致动画结束瞬间“弹回”起点,产生视觉跳变;真正无缝需双份内容+精确位移对齐(如translateX(-320px))+linear时序+硬件加速。

为什么用 @keyframes 做无限滚动容易卡顿或跳帧
直接写 from { transform: translateX(0); } → to { transform: translateX(-100%); } 看似合理,但浏览器在动画结束瞬间会把元素“弹回”起点,造成视觉跳变。这不是 CSS 动画能力不足,而是没处理好位移衔接点。
关键在于:必须让动画终点和起点的视觉状态完全一致,且过渡不可察觉。
- 确保滚动容器宽度 ≥ 所有子元素总宽(否则内容未铺满就位移,会露白底)
- 动画时长需与内容总宽 / 单帧速度匹配,不能硬设固定秒数
- 必须用
transform: translateX(),避免触发布局重排;禁用left或margin-left
如何用两份相同内容实现无缝循环
单份内容滚动到末尾时必然暴露空白,最稳定的做法是复制一份内容并拼接,让动画位移距离刚好等于单份内容宽度,这样第二份内容刚出现时,第一份内容恰好移出视口,视觉上连贯。
HTML 结构示例:
立即学习“前端免费学习笔记(深入)”;
<div class="marquee">
<div class="marquee-content">
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
<!-- 复制一遍 -->
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
</div>
</div>- 用
display: inline-block或flex排列子项,确保.marquee-content自动撑宽 - 通过
getBoundingClientRect().width或 CSSwidth: max-content获取单份内容宽度(用于后续动画计算) - 动画
to关键帧的translateX值必须等于单份内容宽度的负值,例如translateX(-320px),不是-100%
@keyframes 中该用 100% 还是 to?
用 to 没问题,但更推荐显式写 0%, 100% —— 因为某些旧版 Safari 对 to 解析不稳定,且调试时加中间帧(比如 50%)必须依赖百分比语法。
正确写法示例:
@keyframes scroll-loop {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-320px); /* 必须是具体像素值 */
}
}- 禁止在
@keyframes里写100% { transform: translateX(-100%); }—— 百分比是相对于自身尺寸,而自身尺寸在动画中不变,结果就是位移量恒为 0 - 如果内容宽度动态变化(如响应式),需用 JS 重设
animation的duration和关键帧值,纯 CSS 无法响应 - 加
animation-timing-function: linear,避免默认ease导致首尾变速、节奏不稳
如何让鼠标悬停时暂停、移开继续
CSS 动画暂停不能只靠 animation-play-state: paused 在 hover 上,因为一旦暂停,transform 会停留在当前帧,再 resume 时可能从中间位置突兀启动。
- 必须配合
animation-delay动态调整,让恢复时从逻辑起点继续(实际常用 trick 是重置动画:用 JS 切换 class 触发重播) - 更稳妥做法:hover 时设置
animation-duration: 0s,移开时恢复原 duration —— 浏览器会立即跳转到对应进度,视觉无跳跃 - 若需精确控制暂停位置(如对齐文字边界),得用 JS 监听
animationiteration事件 +getComputedStyle读取当前transform矩阵,复杂度陡增
真正难的不是写出来,而是让多行文本、不同字体、缩放屏幕下都保持匀速不抖 —— 那得测真机、看渲染帧率、甚至改用 requestAnimationFrame + canvas 才压得住。


















