轮播图大家应该都不陌生了吧,今天我们来实现一个不一样的轮播图。
效果展示

codePen

代码实现
html
左右切换按钮
html
<div class="nav-btn prev" onclick="rotate(-1,true)">❮</div>
<div class="nav-btn next" onclick="rotate(1,true)">❯</div>

图片容器
html
<div class="spinner"></div>

底部指示器
html
<div class="indicators"></div>

css
全局变量与基础布局
css
:root {
--max-width: 180px; /* 图片最大宽度 */
--perspective: 1200px; /* 透视距离,数值越大,3D效果越平缓 */
--radius: 450px; /* 图片环绕的圆半径(控制图片间距和纵深) */
}
body {
display: flex; /* 居中布局 */
min-height: 100vh; /* 占满视口高度 */
background-color: #f0f0f0; /* 背景色 */
}
- :root:使用 CSS 变量定义可复用的参数,方便后期修改。
- perspective:设置在父容器上,模拟观看 3D 场景的距离,数值越大,图片变形越小(类似长焦镜头)。
3D 旋转容器
css
.spinner {
transform-style: preserve-3d; /* 子元素保持3D变换 */
transform-origin: 50% 50% calc(-1 * var(--radius)); /* 旋转中心点:水平垂直居中,Z轴负方向radius距离 */
height: 300px;
transition: transform 0.8s cubic-bezier(0.23, 1, 0.32, 1); /* 旋转过渡动画, cubic-bezier 定义缓动效果 */
}
- transform-origin:设置旋转中心点在容器中心的正后方(Z 轴负方向),使图片围绕一个虚拟的圆心排列。
- transition :使用 cubic-bezier(0.23, 1, 0.32, 1) 实现 "先快后慢" 的弹性效果,让旋转更自然。
图片样式
css
.spinner img {
position: absolute;
left: 50%; top: 50%; /* 定位到容器中心 */
width: var(--max-width);
transform-origin: 50% 50% calc(-1 * var(--radius)); /* 图片自身的旋转中心点与.spinner一致 */
backface-visibility: hidden; /* 隐藏图片背面,防止旋转时图片翻转显示 */
box-shadow: 0 10px 25px rgba(0,0,0,0.15); /* 投影增强立体感 */
}
- backface-visibility: hidden:关键属性!防止图片旋转到背面时出现镜像翻转,确保始终显示正面。
- transform-origin:与父容器 .spinner 的旋转中心一致,确保图片围绕同一圆心排列。
导航按钮和指示器
css
.nav-btn {
background-color: rgba(255,255,255,0.8); /* 半透明背景 */
box-shadow: 0 2px 10px rgba(0,0,0,0.1); /* 悬浮阴影 */
cursor: pointer;
z-index: 10; /* 确保按钮在图片上方 */
}
.indicator {
width: 10px; height: 10px;
background-color: rgba(0,0,0,0.2); /* 未激活时的指示器样式 */
transition: background-color 0.3s; /* 颜色过渡动画 */
}
.indicator.active {
background-color: rgba(0,0,0,0.6); /* 激活时的指示器样式 */
}
功能实现
配置参数
javascript
const config = {
maxWidth: "180px",
speed: 1500, // 轮播速度
multiple: 1.2, // 鼠标悬停放大倍数
autoPlay: true, // 是否自动轮播
imgList: [
"https://img0.baidu.com/it/u=1446729335,4267600834&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
"https://img2.baidu.com/it/u=2922694860,2270800253&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
"https://img0.baidu.com/it/u=595403291,2269048245&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
..................
],
};
- maxWidth:每个图片的最大宽度
- speed:轮播速度,切换的时间频率(ms)
- multiple:鼠标悬停在图片上的时候,图片放大的倍数
- autoPlay:是否开启自动轮播
- imgList:轮播的图片列表
动态创建图片和指示器
根据配置中的图片列表来动态创建图片元素,并设置好旋转角度,根据图片个数创建对于长度的指示器。
javascript
config.imgList.forEach((src, index) => {
const img = document.createElement("img");
img.src = src;
img.dataset.index = index;
// 计算每个图片的旋转角度
const rotateAngle = -degreesPerImage * index;
img.style.transform = `rotateY(${rotateAngle}deg) translateZ(450px)`;
img.addEventListener("mouseenter", handleMouseEnter);
img.addEventListener("mouseleave", handleMouseLeave);
img.addEventListener("click", () =>
handleImageClick((totalImages - index) % totalImages)
);
spinner.appendChild(img);
// 创建指示器
const indicator = document.createElement("div");
indicator.className = "indicator" + (index === 0 ? " active" : "");
indicator.dataset.index = index;
indicator.addEventListener("click", () => goToSlide(index));
indicators.appendChild(indicator);
});
自动轮播
根据配置信息中的autoPlay
判断是否自动轮播,并在鼠标悬停展示图片的时候停止自动轮播。
javascript
function startAutoPlay() {
if (mouseHover || !config.autoPlay) return;
autoPlayInterval = setInterval(() => {
if (!mouseHover) {
rotate(1);
}
}, config.speed);
}
停止自动轮播
鼠标悬停展示图片、指示器切换图片、左右按钮切换图片的时候需要停止自动轮播,清除自动轮播的定时器即可。
javascript
function stopAutoPlay() {
clearInterval(autoPlayInterval);
autoPlayInterval = null;
}
鼠标移入事件
其实就是图片的hover
效果,在鼠标移入图片的时候放大图片,停止图片自动轮播。
javascript
function handleMouseEnter(e) {
const img = e.target;
img.style.transform = `${img.style.transform} scale(${config.multiple})`;
img.style.boxShadow = "0 15px 30px rgba(0, 0, 0, 0.25)";
img.style.zIndex = "10";
mouseHover = true;
stopAutoPlay();
}
鼠标离开事件
鼠标移出图片的时候恢复图片原状,并继续自动轮播图片。
javascript
function handleMouseLeave(e) {
const img = e.target;
const rotateAngle = -degreesPerImage * img.dataset.index;
img.style.transform = `rotateY(${rotateAngle}deg) translateZ(450px)`;
img.style.boxShadow = "0 10px 25px rgba(0, 0, 0, 0.15)";
img.style.zIndex = "1";
mouseHover = false;
startAutoPlay();
}
图片点击事件
点击图片的时候停止自动轮播,并将被点击的图片移动到最前面展示。
javascript
function handleImageClick(index) {
stopAutoPlay();
goToSlide(index);
}
图片切换逻辑
javascript
function rotate(direction) {
const newIndex = (currentIndex + direction + totalImages) % totalImages; // 计算目标索引(处理循环切换)
const angleDiff = newIndex - currentIndex;
// 选择最短旋转路径(顺时针或逆时针)
if (angleDiff > totalImages / 2) angleDiff -= totalImages;
if (angleDiff < -totalImages / 2) angleDiff += totalImages;
currentIndex = newIndex;
angle += angleDiff * degreesPerImage; // 更新总旋转角度
updateSlide(); // 应用旋转
}
function updateSlide() {
spinner.style.transform = `rotateY(${-angle}deg)`; // 旋转容器,图片随之移动
// 更新指示器状态
indicators.querySelectorAll(".indicator").forEach((ind, i) => {
ind.classList.toggle("active", i === currentIndex);
});
}
- 最短路径算法 :例如 5 张图时,从索引 4 切换到 0 ,最短路径是逆时针转 72° (而非顺时针转 288°)。
- 负角度旋转 :rotateY(${-angle}deg) 确保旋转方向与图片排列方向一致(顺时针切换)。
键盘控制
监听键盘方向键,可以通过左右方向键切换图片。
javascript
window.addEventListener("keydown", (e) => {
if (e.key === "ArrowLeft") rotate(-1, true);
if (e.key === "ArrowRight") rotate(1, true);
});
源码
gitee

github

- 🌟 觉得有帮助的可以点个 star~
- 🖊 有什么问题或错误可以指出,欢迎 pr~
- 📬 有什么想要实现的功能或想法可以联系我~
公众号
关注公众号『 前端也能这么有趣 』,获取更多有趣内容。
发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~
说在后面
🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『
前端也能这么有趣
』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。