实现一个3D轮播图

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

效果展示

codePen

codepen.io/yongtaozhen...

代码实现

html

左右切换按钮

html 复制代码
<div class="nav-btn prev" onclick="rotate(-1,true)">&#10094;</div>
<div class="nav-btn next" onclick="rotate(1,true)">&#10095;</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

gitee.com/zheng_yongt...

github

github.com/yongtaozhen...


  • 🌟 觉得有帮助的可以点个 star~
  • 🖊 有什么问题或错误可以指出,欢迎 pr~
  • 📬 有什么想要实现的功能或想法可以联系我~

公众号

关注公众号『 前端也能这么有趣 』,获取更多有趣内容。

发送 加群 还可以加入群聊,一起来学习(摸鱼)吧~

说在后面

🎉 这里是 JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球 🏸 ,平时也喜欢写些东西,既为自己记录 📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解 🙇,写错的地方望指出,定会认真改进 😊,偶尔也会在自己的公众号『前端也能这么有趣』发一些比较有趣的文章,有兴趣的也可以关注下。在此谢谢大家的支持,我们下文再见 🙌。

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax