话不多说,直接干货
1.css样式表
css
/* === 轮播图样式 === */
.carousel-container {
position: relative; /* 为绝对定位的按钮和指示器提供参考 */
max-width: 800px;
margin: 20px auto;
overflow: hidden; /* 隐藏超出容器的部分 */
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.carousel {
display: flex; /* 核心:让图片水平排列 */
transition: transform 0.5s ease-in-out; /* 核心:为切换添加平滑的滑动动画 */
}
.carousel-item {
min-width: 100%; /* 核心:每个项目占满整个容器宽度 */
height: 300px;
display: flex;
justify-content: center;
align-items: center;
font-size: 2.5rem;
font-weight: bold;
color: white;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
.carousel-item img {
width: 100%;
height: 300px;
}
/* 轮播图按钮样式 */
.carousel-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background-color: rgba(0, 0, 0, 0.5);
color: white;
border: none;
padding: 15px 20px;
font-size: 1.2rem;
cursor: pointer;
border-radius: 0 4px 4px 0; /* 左边按钮圆角在右侧 */
transition: background-color 0.3s;
z-index: 10;
}
.carousel-btn:hover {
background-color: rgba(0, 0, 0, 0.8);
}
.prev-btn {
left: 0;
border-radius: 0 4px 4px 0;
}
.next-btn {
right: 0;
border-radius: 4px 0 0 4px;
}
/* 底部指示器样式 */
.carousel-indicators {
position: absolute;
bottom: 15px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 10;
}
.indicator {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5);
cursor: pointer;
transition: background-color 0.3s, transform 0.3s;
}
.indicator.active {
background-color: white;
transform: scale(1.2); /* 激活的指示器稍大 */
}
2.js轮播逻辑
javascript
// ==================== 轮播图功能 (无缝滚动版) ====================
document.addEventListener("DOMContentLoaded", () => {
console.log("轮播图功能");
// --- 1. 获取DOM元素 ---
const carousel = document.querySelector(".carousel");
const items = document.querySelectorAll(".carousel-item"); // 注意:这是初始的items
const prevBtn = document.querySelector(".prev-btn");
const nextBtn = document.querySelector(".next-btn");
const indicatorsContainer = document.querySelector(".carousel-indicators");
// --- 2. 初始化变量 ---
let currentIndex = 0; // 当前显示的图片索引 (基于原始列表)
const totalItems = items.length;
let autoPlayInterval;
let isTransitioning = false; // 防止在动画过程中重复点击
// --- 3. 克隆首尾元素以实现无缝滚动 ---
const firstItemClone = items[0].cloneNode(true);
const lastItemClone = items[totalItems - 1].cloneNode(true);
// 将克隆项添加到DOM中
carousel.appendChild(firstItemClone);
carousel.insertBefore(lastItemClone, items[0]);
// 重新获取所有items,因为DOM已经改变
const newItems = document.querySelectorAll(".carousel-item");
const newTotalItems = newItems.length;
// 初始化位置,显示第一个真实项 (即克隆的最后一项之后的那一项)
carousel.style.transform = `translateX(-${100}%)`;
// --- 4. 创建指示器 (基于原始数量) ---
items.forEach((_, index) => {
// 注意:这里依然用原始的items来循环
const indicator = document.createElement("div");
indicator.classList.add("indicator");
if (index === 0) {
indicator.classList.add("active");
}
indicator.addEventListener("click", () => {
if (isTransitioning) return;
goToSlide(index);
resetAutoPlay();
});
indicatorsContainer.appendChild(indicator);
});
const indicators = document.querySelectorAll(".indicator");
// --- 5. 核心函数 ---
/**
* 更新轮播图位置和指示器状态
* @param {number} index - 目标图片的索引 (基于原始列表)
* @param {boolean} disableTransition - 是否禁用过渡动画(用于瞬间跳转)
*/
const updateCarousel = (index, disableTransition = false) => {
if (disableTransition) {
carousel.style.transition = "none";
} else {
carousel.style.transition = "transform 0.5s ease-in-out";
}
// 计算偏移量。注意:因为前面多了一个克隆项,所以是 (index + 1)
const offset = -(index + 1) * 100;
carousel.style.transform = `translateX(${offset}%)`;
// 更新指示器状态
indicators.forEach((indicator, i) => {
indicator.classList.toggle("active", i === index);
});
};
/**
* 跳转到指定的幻灯片
* @param {number} index - 目标索引 (基于原始列表)
*/
const goToSlide = (index) => {
if (index < 0 || index >= totalItems) return;
currentIndex = index;
updateCarousel(currentIndex);
};
/**
* 处理滑动到下一张的逻辑
*/
const nextSlide = () => {
if (isTransitioning) return;
isTransitioning = true;
currentIndex++;
updateCarousel(currentIndex);
// 如果滚动到了克隆的第一项,动画结束后跳转到真实的第一项
if (currentIndex === totalItems) {
setTimeout(() => {
currentIndex = 0;
updateCarousel(currentIndex, true); // 瞬间跳转,无动画
isTransitioning = false;
}, 500); // 这个时间必须和CSS中的transition时间一致
} else {
setTimeout(() => {
isTransitioning = false;
}, 500);
}
};
/**
* 处理滑动到上一张的逻辑
*/
const prevSlide = () => {
if (isTransitioning) return;
isTransitioning = true;
currentIndex--;
updateCarousel(currentIndex);
// 如果滚动到了克隆的最后一项,动画结束后跳转到真实的最后一项
if (currentIndex === -1) {
setTimeout(() => {
currentIndex = totalItems - 1;
updateCarousel(currentIndex, true); // 瞬间跳转,无动画
isTransitioning = false;
}, 500); // 这个时间必须和CSS中的transition时间一致
} else {
setTimeout(() => {
isTransitioning = false;
}, 500);
}
};
// --- 自动播放、停止、重置函数 (与之前相同) ---
const startAutoPlay = () => {
autoPlayInterval = setInterval(nextSlide, 3000);
};
const stopAutoPlay = () => {
clearInterval(autoPlayInterval);
};
const resetAutoPlay = () => {
stopAutoPlay();
startAutoPlay();
};
// --- 6. 绑定事件监听器 ---
nextBtn.addEventListener("click", () => {
if (isTransitioning) return;
nextSlide();
resetAutoPlay();
});
prevBtn.addEventListener("click", () => {
if (isTransitioning) return;
prevSlide();
resetAutoPlay();
});
carousel.addEventListener("mouseenter", stopAutoPlay);
carousel.addEventListener("mouseleave", startAutoPlay);
// 监听过渡结束事件,这是一种更健壮的方式
carousel.addEventListener("transitionend", () => {
isTransitioning = false;
});
// --- 7. 初始化 ---
startAutoPlay();
});
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>轮播图</title>
<link rel="stylesheet" href="./swiper.css" />
<script lang="javascript" src="./swiper.js"></script>
</head>
<body>
<!-- 轮播图 -->
<section class="demo-section">
<h2>可控轮播图</h2>
<div class="demo-content">
<p>这是一个支持自动播放、手动切换和指示器跳转的轮播图。</p>
<div class="carousel-container">
<!-- 轮播图主体 -->
<div class="carousel">
<!-- 轮播图项,这里用背景色和文字代替图片 -->
<div class="carousel-item active">
<img
src="https://res.cloudinary.com/dx6iqjba3/image/upload/v1755157950/zjj_x07psy.png"
alt=""
/>
</div>
<div class="carousel-item">
<img
src="https://res.cloudinary.com/dx6iqjba3/image/upload/v1764596177/yushu_zy5o3a.png"
alt=""
/>
</div>
<div class="carousel-item">
<img
src="https://res.cloudinary.com/dx6iqjba3/image/upload/v1764596745/hongbei_wpxxq0.png"
alt=""
/>
</div>
<div class="carousel-item">
<img
src="https://res.cloudinary.com/dx6iqjba3/image/upload/v1755160584/jrsp_p1mapm.png"
alt=""
/>
</div>
</div>
<!-- 左右切换按钮 -->
<button class="carousel-btn prev-btn">❮</button>
<button class="carousel-btn next-btn">❯</button>
<!-- 底部指示器 -->
<div class="carousel-indicators">
<!-- 指示器将由JS动态生成 -->
</div>
</div>
</div>
</section>
</body>
</html>
轮播图虽然是一个常见的UI组件,但其中蕴含的DOM操作、CSS动画和状态管理技巧对前端开发者来说是很好的学习材料。理解这些原理有助于我们在日常开发中创建更高效、更健壮的交互组件。