写在前面
在一些网站里,经常能看到"项目展示"、"合作伙伴 Logo 墙"、"新闻滚动条"这类横向滚动的展示效果。
今天分享一个用 HTML + CSS + JS 实现的简单案例,效果自然流畅,还支持悬停暂停 ,效果如下: 
1. 基础结构
HTML 结构很简单,可以理解为三层:
- 最外层容器:负责限制可见范围。
- 滚动内容容器:用于包裹滚动的元素。
- 具体的滚动项:展示的卡片(图片+文字)。
源码如下:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>横向自动滚动(不使用rem)</title>
<style>
/* 基础样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Arial', sans-serif;
}
body {
background-color: #f5f5f5;
padding: 30px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h2 {
color: #333;
text-align: center;
margin-bottom: 30px;
font-size: 32px;
}
/* 滚动容器样式 */
.scroll-container {
overflow: hidden;
position: relative;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 30px;
}
.scroll-content2 {
display: flex;
}
/* 滚动内容样式 */
.scroll-content {
display: flex;
animation: scroll 3s linear infinite;
}
/* 滚动项样式 */
.scroll-item {
width: 300px;
margin-right: 24px;
flex-shrink: 0;
background-color: #f0f7ff;
border-radius: 6px;
padding: 15px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease;
}
.scroll-item:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.scroll-item img {
width: 100%;
height: 180px;
object-cover: cover;
border-radius: 4px;
margin-bottom: 15px;
}
.scroll-item h3 {
color: #333;
font-size: 18px;
margin-bottom: 8px;
}
.scroll-item p {
color: #666;
font-size: 14px;
}
/* 滚动动画 */
@keyframes scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%); /* 减去一半间距以实现无缝滚动 */
}
}
/* 暂停动画的类 */
.scroll-paused {
animation-play-state: paused;
}
.info-text {
text-align: center;
color: #666;
margin-top: 20px;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h2>横向自动滚动展示</h2>
<!-- 滚动容器 -->
<div class="scroll-container" id="scrollContainer">
<div class="scroll-content2">
<div class="scroll-content" id="scrollContent">
<!-- 滚动项 - 第一组 -->
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=1" alt="示例图片1" />
<h3>项目 1</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=2" alt="示例图片2" />
<h3>项目 2</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=3" alt="示例图片3" />
<h3>项目 3</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=4" alt="示例图片4" />
<h3>项目 4</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=5" alt="示例图片5" />
<h3>项目 5</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
</div>
<!-- 重复第一组项目以实现无缝滚动效果 -->
<div class="scroll-content" id="scrollContent2">
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=1" alt="示例图片1" />
<h3>项目 1</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=2" alt="示例图片2" />
<h3>项目 2</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=3" alt="示例图片3" />
<h3>项目 3</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=4" alt="示例图片4" />
<h3>项目 4</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
<div class="scroll-item">
<img src="https://picsum.photos/400/200?random=5" alt="示例图片5" />
<h3>项目 5</h3>
<p>这是一个滚动展示的项目示例</p>
</div>
</div>
</div>
</div>
<p class="info-text">鼠标悬停在滚动区域上可暂停滚动</p>
</div>
<script>
// 获取滚动容器和内容元素
const scrollContainer = document.getElementById('scrollContainer')
const scrollContent = document.getElementById('scrollContent') // 容器一
const scrollContent2 = document.getElementById('scrollContent2')//容器二
// 鼠标悬停时暂停滚动
scrollContainer.addEventListener('mouseenter', () => {
scrollContent.classList.add('scroll-paused')
scrollContent2.classList.add('scroll-paused')
})
// 鼠标离开时继续滚动
scrollContainer.addEventListener('mouseleave', () => {
scrollContent.classList.remove('scroll-paused')
scrollContent2.classList.remove('scroll-paused')
})
</script>
</body>
</html>
📌 这里的关键点是:滚动内容要写两份,第一份结束时第二份紧跟上,就能形成"无缝衔接"的效果。、
大致过程如下:
首先是两组相同内容并排放置 动画让整体向左移动 移动距离 = 一组内容的宽度 第一组移出时第二组正好进入 动画结束时重置到初始状态 无限循环形成连续滚动

2. 样式拆解
核心样式在于三个部分:
- 容器限制宽度 + 隐藏溢出
css
.scroll-container {
overflow: hidden;
position: relative;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 30px;
}
这样只会看到一屏的内容,超出的部分会被隐藏。
- 滚动内容横向排列
css
.scroll-content {
display: flex;
animation: scroll 20s linear infinite;
}
设置 display: flex 后,子元素会自然横排,再配合 animation 实现自动滚动。
- 动画关键帧
css
@keyframes scroll {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
整个内容不断向左平移,从 0 移动到 -100% 的位置,循环播放。
3. 鼠标悬停暂停
为了用户体验,最好加一个"悬停暂停"。
通过 JS 控制 animation-play-state:
JS
// 获取滚动容器和内容元素
const scrollContainer = document.getElementById('scrollContainer')
const scrollContent = document.getElementById('scrollContent') // 容器一
const scrollContent2 = document.getElementById('scrollContent2') // 容器2
// 鼠标悬停时暂停滚动
scrollContainer.addEventListener('mouseenter', () => {
scrollContent.classList.add('scroll-paused')
scrollContent2.classList.add('scroll-paused')
})
// 鼠标离开时继续滚动
scrollContainer.addEventListener('mouseleave', () => {
scrollContent.classList.remove('scroll-paused')
scrollContent2.classList.remove('scroll-paused')
})
CSS 中加一个辅助类:
css
.scroll-paused {
animation-play-state: paused;
}
这样当鼠标移上去时,滚动会停下来,移开后继续。
5. 小结与优化思路
- 适用场景:展示案例、Logo 墙、新闻条、商品推荐等。
- 无缝循环:记得复制一份内容,不然会出现"空白断层"。
- 流畅性 :尽量用
transform: translateX,性能更好。 - 交互优化:可以加上触摸滑动、点击切换等功能,适配移动端。