我们将创建一个功能齐全的照片轮播组件,具备以下特点:

(图片来源网络,侵删)
- 自动播放
- 左右箭头手动切换
- 底部圆点指示器
- 鼠标悬停时暂停
- 平滑的过渡动画
使用原生 JavaScript (推荐理解原理)
这种方法不依赖任何外部库,能帮助你最好地理解轮播的底层工作原理。
最终效果预览
实现步骤
第 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">原生 JS 轮播图</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="carousel-container">
<!-- 轮播图片列表 -->
<div class="carousel-slide">
<img src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 1">
<img src="https://images.unsplash.com/photo-1543857778-c4a1a569e7bd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 2">
<img src="https://images.unsplash.com/photo-1572241972377-87b9d6f4e896?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 3">
<img src="https://images.unsplash.com/photo-1587588354457-6f13a8cd8b48?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 4">
<img src="https://images.unsplash.com/photo-1591926759938-b6fa64b6cd54?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 5">
</div>
<!-- 左右箭头 -->
<button class="carousel-btn prev-btn">❮</button>
<button class="carousel-btn next-btn">❯</button>
<!-- 底部指示器 -->
<div class="carousel-indicators"></div>
</div>
<script src="script.js"></script>
</body>
</html>
第 2 步:CSS 样式

(图片来源网络,侵删)
CSS 的关键在于:
- 将所有图片水平排列在一行。
- 使用
overflow: hidden来隐藏容器外的图片,只显示当前图片。 - 通过
transform: translateX()来移动整个图片列表,实现切换效果。
/* style.css */
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f4;
}
.carousel-container {
position: relative;
width: 800px; /* 根据你的图片尺寸调整 */
height: 450px; /* 根据你的图片尺寸调整 */
overflow: hidden;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.carousel-slide {
display: flex;
transition: transform 0.5s ease-in-out; /* 平滑过渡效果 */
}
.carousel-slide img {
width: 100%;
height: 100%;
object-fit: cover; /* 确保图片填满容器且不变形 */
}
/* 左右箭头样式 */
.carousel-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
padding: 16px;
cursor: pointer;
font-size: 18px;
border-radius: 50%;
transition: background-color 0.3s;
}
.carousel-btn:hover {
background-color: rgba(0, 0, 0, 0.8);
}
.prev-btn {
left: 10px;
}
.next-btn {
right: 10px;
}
/* 指示器样式 */
.carousel-indicators {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5);
cursor: pointer;
transition: background-color 0.3s;
}
.dot.active {
background-color: white;
}
**第 3 步:JavaScript 逻辑 (核心部分)
这是轮播图的大脑,负责处理用户交互和自动播放逻辑。
// script.js
document.addEventListener('DOMContentLoaded', () => {
const carouselSlide = document.querySelector('.carousel-slide');
const images = document.querySelectorAll('.carousel-slide img');
const prevBtn = document.querySelector('.prev-btn');
const nextBtn = document.querySelector('.next-btn');
const indicatorsContainer = document.querySelector('.carousel-indicators');
// 计算图片数量和宽度
const imageCount = images.length;
const imageWidth = images[0].clientWidth;
let counter = 0;
// 克隆第一张图片并添加到末尾,实现无缝循环
const firstClone = images[0].cloneNode(true);
carouselSlide.appendChild(firstClone);
// 创建底部指示器
for (let i = 0; i < imageCount; i++) {
const dot = document.createElement('span');
dot.classList.add('dot');
if (i === 0) dot.classList.add('active');
dot.addEventListener('click', () => {
counter = i;
updateCarousel();
updateIndicators();
});
indicatorsContainer.appendChild(dot);
}
const dots = document.querySelectorAll('.dot');
// 更新轮播图位置
function updateCarousel() {
carouselSlide.style.transform = `translateX(${-counter * imageWidth}px)`;
}
// 更新指示器状态
function updateIndicators() {
dots.forEach(dot => dot.classList.remove('active'));
dots[counter % imageCount].classList.add('active');
}
// 点击下一张
nextBtn.addEventListener('click', () => {
if (counter >= imageCount) {
// 如果是克隆的图片,瞬间跳回第一张
carouselSlide.style.transition = 'none';
counter = 0;
updateCarousel();
// 强制重绘,然后应用过渡效果并移动到第二张
setTimeout(() => {
carouselSlide.style.transition = 'transform 0.5s ease-in-out';
counter++;
updateCarousel();
updateIndicators();
}, 20);
} else {
counter++;
updateCarousel();
updateIndicators();
}
});
// 点击上一张
prevBtn.addEventListener('click', () => {
if (counter === 0) {
// 如果是第一张,瞬间跳到克隆的那张(最后一张)
carouselSlide.style.transition = 'none';
counter = imageCount;
updateCarousel();
// 强制重绘,然后应用过渡效果并移动到倒数第二张
setTimeout(() => {
carouselSlide.style.transition = 'transform 0.5s ease-in-out';
counter--;
updateCarousel();
updateIndicators();
}, 20);
} else {
counter--;
updateCarousel();
updateIndicators();
}
});
// 自动播放功能
let slideInterval = setInterval(() => {
nextBtn.click();
}, 3000); // 每3秒切换一次
// 鼠标悬停时暂停
const carouselContainer = document.querySelector('.carousel-container');
carouselContainer.addEventListener('mouseenter', () => {
clearInterval(slideInterval);
});
// 鼠标离开时继续播放
carouselContainer.addEventListener('mouseleave', () => {
slideInterval = setInterval(() => {
nextBtn.click();
}, 3000);
});
});
使用 CSS has() 选择器 (现代、简洁)
如果你的项目环境支持较新的浏览器,可以使用 CSS 的 has() 选择器来实现一个非常简洁的轮播图,无需 JavaScript。

(图片来源网络,侵删)
HTML 结构 (与方法一相同)
CSS 样式
/* style.css */
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f4f4f4;
}
.carousel-container {
position: relative;
width: 800px;
height: 450px;
overflow: hidden;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.carousel-slide {
display: flex;
transition: transform 0.5s ease-in-out;
}
/* 隐藏所有图片,除了当前激活的 */
.carousel-slide img:not(:first-child) {
display: none;
}
/* 当容器拥有 .active-dot 类时,显示对应的图片 */
/* :nth-child(n+2) 是为了跳过第一张图片,从第二张开始计数 */
.carousel-container:has(.active-dot:nth-child(2)) .carousel-slide img:nth-child(2) { display: block; }
.carousel-container:has(.active-dot:nth-child(3)) .carousel-slide img:nth-child(3) { display: block; }
.carousel-container:has(.active-dot:nth-child(4)) .carousel-slide img:nth-child(4) { display: block; }
.carousel-container:has(.active-dot:nth-child(5)) .carousel-slide img:nth-child(5) { display: block; }
.carousel-container:has(.active-dot:nth-child(6)) .carousel-slide img:nth-child(6) { display: block; }
.carousel-slide img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 箭头样式 */
.carousel-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
padding: 16px;
cursor: pointer;
font-size: 18px;
border-radius: 50%;
transition: background-color 0.3s;
z-index: 10;
}
.carousel-btn:hover {
background-color: rgba(0, 0, 0, 0.8);
}
.prev-btn {
left: 10px;
}
.next-btn {
right: 10px;
}
/* 指示器样式 */
.carousel-indicators {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 10;
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5);
cursor: pointer;
transition: background-color 0.3s;
}
.dot.active {
background-color: white;
}
HTML 结构 (需要一些修改)
为了让 CSS has() 能够工作,我们需要在 HTML 中通过 radio 按钮来控制状态。
<!-- index.html -->
<div class="carousel-container">
<!-- 使用 radio 按钮来控制当前显示的图片 -->
<input type="radio" name="carousel" id="slide1" checked>
<input type="radio" name="carousel" id="slide2">
<input type="radio" name="carousel" id="slide3">
<input type="radio" name="carousel" id="slide4">
<input type="radio" name="carousel" id="slide5">
<div class="carousel-slide">
<img src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 1">
<img src="https://images.unsplash.com/photo-1543857778-c4a1a569e7bd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 2">
<img src="https://images.unsplash.com/photo-1572241972377-87b9d6f4e896?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 3">
<img src="https://images.unsplash.com/photo-1587588354457-6f13a8cd8b48?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 4">
<img src="https://images.unsplash.com/photo-1591926759938-b6fa64b6cd54?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 5">
</div>
<!-- 左右箭头 -->
<label for="slide5" class="carousel-btn prev-btn">❮</label>
<label for="slide2" class="carousel-btn next-btn">❯</label>
<!-- 底部指示器 -->
<div class="carousel-indicators">
<label for="slide1" class="dot active"></label>
<label for="slide2" class="dot"></label>
<label for="slide3" class="dot"></label>
<label for="slide4" class="dot"></label>
<label for="slide5" class="dot"></label>
</div>
</div>
这种方法的优势:
- 无 JavaScript:纯 CSS 实现,代码量少,性能好。
- 简单直观:状态管理通过 HTML 的
radio按钮完成,逻辑清晰。
劣势:
- 浏览器兼容性:
has()选择器在旧版浏览器(如 Edge 18-, Firefox 78-, Safari 15.4-)中不被支持。 - 自动播放实现复杂:实现真正的无限循环自动播放会比较麻烦,通常还是需要一点 JavaScript 来动态切换
radio按钮的checked状态。
使用第三方库 (如 Swiper.js)
在实际项目中,为了快速开发、稳定性和丰富的功能,我们通常会使用成熟的轮播图库。Swiper.js 是目前最受欢迎的选择之一。
优点
- 功能强大:支持触摸滑动、无限循环、分页、滚动条、延迟加载等几乎所有你能想到的功能。
- 高度可定制:提供丰富的 API 和配置选项。
- 性能优异:经过充分优化,在各种设备上都能流畅运行。
- 文档完善:有非常详细的官方文档和示例。
快速上手 Swiper.js
引入 Swiper 的 CSS 和 JS 文件 你可以从 Swiper 官网 下载文件,或使用 CDN。
<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
...
<!-- Swiper CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css" />
<style>
/* 自定义样式,让 Swiper 容器有固定大小 */
.swiper {
width: 800px;
height: 450px;
}
.swiper-slide img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
</head>
<body>
<div class="swiper mySwiper">
<div class="swiper-wrapper">
<div class="swiper-slide">
<img src="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 1">
</div>
<div class="swiper-slide">
<img src="https://images.unsplash.com/photo-1543857778-c4a1a569e7bd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 2">
</div>
<div class="swiper-slide">
<img src="https://images.unsplash.com/photo-1572241972377-87b9d6f4e896?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 3">
</div>
<div class="swiper-slide">
<img src="https://images.unsplash.com/photo-1587588354457-6f13a8cd8b48?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 4">
</div>
<div class="swiper-slide">
<img src="https://images.unsplash.com/photo-1591926759938-b6fa64b6cd54?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1170&q=80" alt="Image 5">
</div>
</div>
<!-- 如果需要分页器 -->
<div class="swiper-pagination"></div>
<!-- 如果需要导航按钮 -->
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
<!-- 如果需要滚动条 -->
<div class="swiper-scrollbar"></div>
</div>
<!-- Swiper JS -->
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script>
// 初始化 Swiper
const swiper = new Swiper(".mySwiper", {
// 配置选项
loop: true, // 循环模式
pagination: {
el: ".swiper-pagination",
clickable: true, // 点击分页器切换
},
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
autoplay: {
delay: 3000, // 3秒切换一次
disableOnInteraction: false, // 用户操作后是否停止自动播放
},
});
</script>
</body>
</html>
总结与选择建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 原生 JavaScript | - 理解原理,无依赖 - 完全可控,轻量 |
- 代码量较大,需要处理很多细节 - 实现复杂功能(如无限循环)较繁琐 |
学习前端、面试、对性能要求极高且不依赖库的项目 |
CSS has() |
- 代码极简,性能好 - 纯 CSS 实现 |
- 浏览器兼容性差 - 实现复杂功能(如自动播放)困难 |
现代Web项目、追求极致简洁和性能,且兼容性要求不高的场景 |
| Swiper.js 等库 | - 功能强大,开箱即用 - 性能优异,稳定可靠 - 文档完善,社区活跃 |
- 引入外部文件,增加项目体积 - 学习库的 API 和配置 |
绝大多数实际项目,尤其是商业项目,能极大提高开发效率 |
对于初学者,强烈建议从方法一(原生 JavaScript)开始,它能让你深刻理解轮播图的实现机制,对于快速开发,直接使用 Swiper.js 是最明智的选择,CSS has() 则是一个展示现代 CSS 强大能力的有趣范例。
