掌握使用 Flex + transform: translateX 实现水平滑动轮播的核心原理
学会用 原生 JavaScript 控制 DOM 状态与动画
实现 手动导航(Prev/Next) 与 自动播放(Auto-play) 双模式
理解 索引边界处理 与 循环切换逻辑
构建 响应式、无障碍友好 的 UI 组件
轮播图(Carousel / Slider) 是一种常见的 Web 组件,用于在有限空间内循环展示多张图片或内容卡片。广泛应用于:
首页横幅广告
产品展示
用户评价轮播
新闻头条滚动
我们选择 第三种方案 —— 通过移动 .img-wrap 容器实现无缝滑动
<div class="carousel">
<div class="image-wrapper">
<img src="image1.jpg" alt="Slide 1">
<img src="image2.jpg" alt="Slide 2">
<img src="image3.jpg" alt="Slide 3">
</div>
<button class="nav-btn prev">‹</button>
<button class="nav-btn next">›</button>
</div>关键点说明:
.carousel:外层容器,设置 overflow: hidden 隐藏超出部分
.image-wrapper:所有图片的父容器,使用 display: flex 横向排列
<img>:每张图片宽度为 100%,高度自适应
.nav-btn:导航按钮,使用语义化 <button> 提升无障碍访问(a11y)
重要:所有图片必须具有相同宽高比,否则滑动时会出现布局跳动。
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.carousel {
position: relative;
width: 80%;
max-width: 600px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.image-wrapper {
display: flex;
transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.image-wrapper img {
width: 100%;
height: auto;
flex-shrink: 0; /* 防止图片被压缩 */
}
.nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0, 0, 0, 0.6);
color: white;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
font-size: 18px;
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
}
.nav-btn:hover {
background: rgba(0, 0, 0, 0.85);
}
.prev { left: 15px; }
.next { right: 15px; }设计亮点:
flex-shrink: 0:确保每张图片保持原始宽度,不被 flex 压缩
cubic-bezier 过渡:比 ease 更自然的滑动曲线
圆形按钮 + 居中图标:现代 UI 风格
z-index 保证按钮在图片上方
// 获取 DOM 元素
const prevBtn = document.querySelector('.prev');
const nextBtn = document.querySelector('.next');
const imageWrapper = document.querySelector('.image-wrapper');
const images = document.querySelectorAll('.image-wrapper img');
let currentIndex = 0;
const totalImages = images.length;
// 更新轮播位置
function updateCarousel() {
// 边界处理:循环切换
if (currentIndex >= totalImages) currentIndex = 0;
if (currentIndex < 0) currentIndex = totalImages - 1;
// 计算位移:每张图占 100%,第 n 张图位移 -n * 100%
imageWrapper.style.transform = `translateX(-${currentIndex * 100}%)`;
}
// 下一张
nextBtn.addEventListener('click', () => {
currentIndex++;
updateCarousel();
});
// 上一张
prevBtn.addEventListener('click', () => {
currentIndex--;
updateCarousel();
});
// 自动播放(每 3 秒切换)
let autoPlayInterval = setInterval(() => {
currentIndex++;
updateCarousel();
}, 3000);
// 可选:鼠标悬停暂停自动播放
const carouselContainer = document.querySelector('.carousel');
carouselContainer.addEventListener('mouseenter', () => {
clearInterval(autoPlayInterval);
});
carouselContainer.addEventListener('mouseleave', () => {
autoPlayInterval = setInterval(() => {
currentIndex++;
updateCarousel();
}, 3000);
});核心机制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>轮播图</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f4f4;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.carousel {
position: relative;
width: 80%;
max-width: 600px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.image-wrapper {
display: flex;
transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.image-wrapper img {
width: 100%;
height: auto;
flex-shrink: 0;
}
.nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0, 0, 0, 0.6);
color: white;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
font-size: 18px;
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
}
.nav-btn:hover {
background: rgba(0, 0, 0, 0.85);
}
.prev { left: 15px; }
.next { right: 15px; }
</style>
</head>
<body>
<div class="carousel">
<div class="image-wrapper">
<img src="https://img1.baidu.com/it/u=36078727,2257383763&fm=253&fmt=auto&app=120&f=JPEG?w=667&h=500" alt="卡提西亚">
<img src="https://img1.baidu.com/it/u=3701525992,1836708913&fm=253&fmt=auto&app=138&f=JPEG?w=888&h=500" alt="夏空">
<img src="https://img1.baidu.com/it/u=2879016026,2518577144&fm=253&fmt=auto&app=120&f=JPEG?w=667&h=500" alt="守岸人">
</div>
<button class="nav-btn prev">‹</button>
<button class="nav-btn next">›</button>
</div>
<script>
const prevBtn = document.querySelector('.prev');
const nextBtn = document.querySelector('.next');
const imageWrapper = document.querySelector('.image-wrapper');
const images = document.querySelectorAll('.image-wrapper img');
let currentIndex = 0;
const totalImages = images.length;
function updateCarousel() {
if (currentIndex >= totalImages) currentIndex = 0;
if (currentIndex < 0) currentIndex = totalImages - 1;
imageWrapper.style.transform = `translateX(-${currentIndex * 100}%)`;
}
nextBtn.addEventListener('click', () => {
currentIndex++;
updateCarousel();
});
prevBtn.addEventListener('click', () => {
currentIndex--;
updateCarousel();
});
// 自动播放
let autoPlayInterval = setInterval(() => {
currentIndex++;
updateCarousel();
}, 3000);
// 悬停暂停
const carousel = document.querySelector('.carousel');
carousel.addEventListener('mouseenter', () => clearInterval(autoPlayInterval));
carousel.addEventListener('mouseleave', () => {
autoPlayInterval = setInterval(() => {
currentIndex++;
updateCarousel();
}, 3000);
});
</script>
</body>
</html>指示器(Dots):底部小圆点显示当前页码
键盘导航:支持 ← → 方向键控制
触摸滑动:监听 touchstart/touchmove/touchend 实现移动端手势
懒加载:仅加载可视区域图片,提升性能
响应式断点:在小屏设备上禁用自动播放
// 示例:添加指示器
// HTML: <div class="dots"></div>
// JS:
// images.forEach((_, i) => {
// const dot = document.createElement('span');
// dot.classList.add('dot');
// dot.addEventListener('click', () => { currentIndex = i; updateCarousel(); });
// document.querySelector('.dots').appendChild(dot);
// });核心原理:transform: translateX + flex 布局实现平滑滑动
状态管理:用 currentIndex 控制当前显示项
循环逻辑:通过模运算或条件判断实现首尾衔接
用户体验:自动播放 + 悬停暂停 + 清晰导航
无障碍基础:使用 <button> 和 alt 属性
当前实现中,如果用户快速连续点击 “Next” 按钮,会发生什么?如何防止动画中断或错乱?
如何修改代码以支持垂直轮播(上下滑动)?
如果图片数量动态变化(如通过 API 加载),如何让轮播图自动适配?