
本文详解如何使用纯 CSS 构建真正无缝的无限滚动轮播组件,通过精确计算内容宽度、改用 transform: translateX() 替代 left 动画,并复制内容实现视觉连续性,彻底消除循环时的“卡顿”或“抖动”现象。
本文详解如何使用纯 css 构建真正无缝的无限滚动轮播组件,通过精确计算内容宽度、改用 `transform: translatex()` 替代 `left` 动画,并复制内容实现视觉连续性,彻底消除循环时的“卡顿”或“抖动”现象。
要实现一个平滑、无限、无感知跳变的 CSS 轮播(marquee),关键在于两点:动画基准必须与内容尺寸严格对齐,且循环过渡必须由重复内容自然承接。原代码中使用 left: -100% 和 width: 200% 的做法依赖父容器百分比,而父容器(.marquee-block)被设为 width: calc(30% * 5),远小于实际内容宽度,导致动画位移失准——这是“抖动”的根本原因。
✅ 正确解法:精准宽度 + transform + 内容镜像
首先,明确 .marquee-item 固定宽为 580px,gap: 40px(由 .marquee-inner span 定义),共 --total-marquee-items: 5 项。单组 <span></span> 的总宽度应为:
5 × 580px(项目宽) + 4 × 40px(4个间隙) = 2900px + 160px = 3060px
但为实现无缝循环,需至少渲染两组完整内容(即两个 <span></span>),因此 .marquee-inner 的宽度应设为:
.marquee-inner {
width: calc((580px + 40px) * var(--total-marquee-items) + 40px);
/* = (620px × 5) + 40px = 3140px —— 精确覆盖 5 项 + 末尾 gap */
}
? 注意:此处
+ 40px是为了补足最后一项后本该存在的gap(因gap仅作用于相邻项之间,5 项产生 4 个 gap,但双组拼接时第二组首项需与第一组末项保持相同间距,故总宽 = 单组宽 × 2 − 末项冗余 gap;更稳妥写法是calc((580px + 40px) * 5),即每项含自身宽+右侧 gap,5 项共占5 × 620px = 3100px,已足够。示例中采用3140px属保守冗余,实际推荐calc(580px * 5 + 40px * 4)或直接3100px)。
其次,动画必须基于元素自身尺寸。将 left 改为 transform: translateX(),因为:
-
transform的%单位参照元素自身宽(而非父容器),translateX(-100%)精确等于向左移动一个自身宽度; - GPU 加速,性能更优,避免重排重绘导致的卡顿。
修正后的关键动画规则如下:
@keyframes marqueeLeft {
0% { transform: translateX(0); }
100% { transform: translateX(-3100px); } /* 推荐显式像素值,完全可控 */
}
/* 或更通用(依赖 CSS 变量): */
@keyframes marqueeLeft {
0% { transform: translateX(0); }
100% { transform: translateX(calc(-1 * (580px * 5 + 40px * 4))); }
}
⚠️ 强烈建议避免
translateX(-100%):虽然语义清晰,但在多组<span></span>布局下,-100%指的是.marquee-inner自身宽度(如3140px),而实际需位移的距离是单组内容宽度(3100px)。若两者不等,终点位置偏差,就会在循环起点暴露空白或重叠——这正是“抖动”的视觉来源。
✅ HTML 结构:双组内容保障无缝衔接
原结构已正确采用双 <span></span> 复制内容(如 1-2-3-4-5 + 1-2-3-4-5),这是实现无限滚动的基石。当第一组滚动出视口时,第二组恰好完全进入,视觉上无中断。务必确保:
- 两组
<span></span>内容完全一致且顺序相同(对to-left方向); -
.marquee-inner高度足够容纳单行内容(当前height: 150px合理); -
.marquee-block设置overflow: hidden严格裁剪。
✅ 完整可运行示例(精简优化版)
<meta charset="UTF-8"><title>Seamless CSS Marquee</title><style>
.marquee-wrapper { background: #2F394C; text-align: center; }
.marquee-wrapper .container { overflow: hidden; }
.marquee-inner span { display: flex; gap: 40px; }
.marquee-block {
--total: 5;
height: 150px;
margin: 20px auto;
background: #1B2531;
padding: 30px 0;
overflow: hidden;
position: relative;
}
.marquee-inner {
display: flex;
width: calc(580px * 5 + 40px * 4); /* 3100px */
position: absolute;
animation: marqueeLeft 5s linear infinite;
}
@keyframes marqueeLeft {
0% { transform: translateX(0); }
100% { transform: translateX(-3100px); }
}
.marquee-item {
width: 580px;
background: #00cc00;
flex-shrink: 0; /* 关键:禁止缩放破坏宽度 */
}
.marquee-item p {
font: 800 30px/1 cursive;
margin: 0;
color: white;
}
</style><div class="marquee-wrapper">
<div class="container">
<div class="marquee-block">
<div class="marquee-inner">
<!-- 第一组 -->
<div class="marquee-item"><p>1</p></div>
<div class="marquee-item"><p>2</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill7883" title="wechat-article-extraction-mp-weixin-qq-com news-webpage-cleaning blog-post-parsing metadata-extraction-title-author-date multiple-output-formats-markdown-json-plain-text batch-processing-support"><img
src="https://img.php.cn/upload/skill/000/000/081/179172015330974.jpg" alt="wechat-article-extraction-mp-weixin-qq-com news-webpage-cleaning blog-post-parsing metadata-extraction-title-author-date multiple-output-formats-markdown-json-plain-text batch-processing-support" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill7883" title="wechat-article-extraction-mp-weixin-qq-com news-webpage-cleaning blog-post-parsing metadata-extraction-title-author-date multiple-output-formats-markdown-json-plain-text batch-processing-support" class="overflowclass">wechat-article-extraction-mp-weixin-qq-com news-webpage-cleaning blog-post-parsing metadata-extraction-title-author-date multiple-output-formats-markdown-json-plain-text batch-processing-support</a>
<p class="overflowclass">基于三引擎设计,从微信文章、新闻和博客网页提取干净内容,支持标题作者日期元数据,多格式和批量处理。</p>
</div>
<a rel="nofollow" href="/xiazai/skill7883" title="wechat-article-extraction-mp-weixin-qq-com news-webpage-cleaning blog-post-parsing metadata-extraction-title-author-date multiple-output-formats-markdown-json-plain-text batch-processing-support" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div></div>
<div class="marquee-item"><p>3</p></div>
<div class="marquee-item"><p>4</p></div>
<div class="marquee-item"><p>5</p></div>
<!-- 第二组(完全相同) -->
<div class="marquee-item"><p>1</p></div>
<div class="marquee-item"><p>2</p></div>
<div class="marquee-item"><p>3</p></div>
<div class="marquee-item"><p>4</p></div>
<div class="marquee-item"><p>5</p></div>
</div>
</div>
</div>
</div>
? 关键总结
-
宽度必须精确:
.marquee-inner宽 =itemWidth × count + gap × (count−1),拒绝模糊百分比; -
动画用
transform:translateX()的%基于自身宽,但显式像素值(如-3100px)更可靠、易调试; - 内容必须双份:两组相同序列确保滚动终点与起点视觉连续;
-
禁用 flex 缩放:添加
flex-shrink: 0到.marquee-item,防止浏览器自动压缩; -
无需 JS、响应式无关:所有尺寸固定(
580px),天然适配需求,且不随屏幕变化。
按此方案实现,轮播将如丝般顺滑循环,彻底告别“一卡一跳”的体验缺陷。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










