💡《我用纯JS撸了个3D轮播图,同事以为我偷偷用了Three.js!》

💡《我用纯JS撸了个3D轮播图,同事以为我偷偷用了Three.js!》

最近在重构公司官网时,产品经理甩过来一个需求:"做个有空间感的轮播图,要那种一眼惊艳的效果"。原本想搬出Three.js,但转念一想------用原生JS+CSS3能不能实现?经过两天爆肝,终于写出了这个丝滑的3D轮播组件。今天就把开发过程中的踩坑实录和实现思路分享给大家!


一、效果预览与核心思路

先看最终效果([在线Demo](3D Carousel)):\

特点一览

  • 近大远小的立体层叠
  • Y轴旋转形成的夹角透视
  • 支持拖拽/按钮双操作模式
  • 流畅的缓动过渡动画

实现秘诀

"用translateX控制水平位移,rotateY制造立体夹角,scale实现近大远小,最后用z-index控制层级------这就是裸眼3D的魔法四要素"


二、手撕关键代码

1. 初始化结构

html 复制代码
<div class="carousel">
  <!-- 7个轮播项 -->
  <div class="carousel-item"><img src="..."></div>
  ...
  <!-- 左右箭头 -->
  <div class="prev-button"></div>
  <div class="next-button"></div>
</div>

运行 HTML

这里有个小细节:按钮用position: absolute定位在轮播容器外部,避免影响3D变换

2. 核心算法------动态布局

js 复制代码
function layout() {
  carouselItems.forEach((item, i) => {
    // 当前项居中,其他项向两侧散开
    const offset = (i - activeIndex) * 200; 
    
    // 越远的元素越小(0.8~1区间)
    const scale = 1 - Math.abs(i-activeIndex)/items.length * 0.2; 
    
    // 左右元素分别向不同方向旋转
    const rotateY = i === activeIndex ? 0 : (i > activeIndex ? -45 : 45);
    
    // 透明度指数衰减(几何级数下降)
    const opacity = Math.pow(0.6, Math.abs(i-activeIndex));
    
    // 应用所有变换!注意顺序:先位移再旋转
    item.style.transform = `translateX(${offset}px) 
                          scale(${scale}) 
                          rotateY(${rotateY}deg)`;
    item.style.opacity = opacity;
  });
}

为什么用translateX而不用left?

因为transform属性能触发GPU加速,动画更流畅,避免重排带来的性能损耗

3. 切换控制

js 复制代码
// 上一张
function playPrev() {
  activeIndex = Math.max(0, activeIndex - 1); // 防左移越界
  layout(); // 触发重新布局
}

// 下一张
function playNext() {
  activeIndex = Math.min(items.length-1, activeIndex + 1); // 防右移越界
  layout();
}

// 绑定按钮事件
prevBtn.addEventListener('click', playPrev);
nextBtn.addEventListener('click', playNext);

边界控制的坑

第一次没做越界判断时,activeIndex变成-1直接导致所有元素右移飞出了屏幕😅

4. 拖拽交互

js 复制代码
let startX = 0;
const threshold = 30; // 触发阈值

carousel.addEventListener('mousedown', e => {
  startX = e.clientX;
  e.preventDefault(); // 防止选中文本
});

document.addEventListener('mouseup', e => {
  const deltaX = e.clientX - startX;
  if (Math.abs(deltaX) < threshold) return;
  
  deltaX > 0 ? playPrev() : playNext();
});

为什么用document监听mouseup?

如果只在carousel上监听,当鼠标快速拖出容器外释放时,会无法触发事件


三、性能优化实战

1. 隐藏远端元素

js 复制代码
// 在layout函数中添加:
if (Math.abs(i - activeIndex) > 2) {
  item.style.display = 'none';
} else {
  item.style.display = 'block';
}

实测FPS从45提升到60!原理是减少不可见元素的样式计算

2. 硬件加速技巧

css 复制代码
.carousel-item {
  will-change: transform; /* 提前告知浏览器可能变化 */
  backface-visibility: hidden; /* 隐藏背面 */
}

3. 防抖处理

js 复制代码
let isAnimating = false;

function playNext() {
  if (isAnimating) return;
  isAnimating = true;
  
  activeIndex++;
  layout();
  
  setTimeout(() => {
    isAnimating = false;
  }, 500); // 匹配动画时长
}

避免快速点击导致的动画错乱


四、扩展玩法

1. 无限循环模式

js 复制代码
// 修改playNext函数
if (activeIndex >= items.length - 1) {
  // 瞬间跳转到克隆的第一个元素
  activeIndex = 0;
  setTimeout(layout, 50); // 等跳转完成再布局
}

2. 3D景深控制

js 复制代码
// 动态调整perspective
carousel.style.perspective = 800 + activeIndex * 100 + 'px';

随着切换改变透视距离,增强空间感

3. 加入光照效果

css 复制代码
.carousel-item {
  box-shadow: 0 10px 20px rgba(0,0,0,0.2);
  filter: brightness(var(--brightness));
}

/* 在layout函数中动态设置亮度 */
item.style.setProperty('--brightness', 
  1 - Math.abs(i-activeIndex)*0.2);

五、写给新手的建议

  1. 调试3D的秘诀

    在浏览器开发者工具中开启"3D视图",实时观察元素的空间位置

  2. 参数调节技巧

    把offsetStep、rotateY角度等变量提取为配置项,方便微调效果

  3. 常见坑点

    • 忘记设置transform-style: preserve-3d导致子元素丢失3D特性
    • z-index计算错误导致层级混乱
    • 透明元素点击穿透问题(可加pointer-events: none解决)

最后的话

这个3D轮播从第一版到最终效果,我重构了3次。最大的感悟是:前端实现复杂效果时,不能只追求代码高级,更要理解底层原理。就像这个案例,本质不过是translateX+rotateY的组合魔法。

GitHub源码 :[the-one-xjb/3DCarousel](: There's a lot of interesting native JS involved)
在线体验: :3D Carousel

如果本文对你有帮助,请点赞收藏❤️,你的支持是我创作的最大动力!

讨论话题:你觉得这个3D效果还能怎么升级?在评论区留下你的创意,点赞最高的想法我会在下期实现!

相关推荐
江沉晚呤时3 分钟前
C# 状态模式深度解析:构建灵活的状态驱动系统
开发语言·javascript·数据库·ui·ajax·c#·ecmascript
2401_8979300615 分钟前
CSS 从入门到精通
前端
还是鼠鼠22 分钟前
Node.js局部生效的中间件
javascript·vscode·中间件·node.js·json·express
好_快39 分钟前
Lodash源码阅读-baseProperty
前端·javascript·源码阅读
每一天,每一步1 小时前
echarts生成3D立体地图react组件
javascript·react.js·echarts
BillKu1 小时前
java后端对时间进行格式处理
java·开发语言·前端
excel1 小时前
webpack 格式化模块工厂 第 二 节
前端
excel1 小时前
webpack 格式化模块工厂 第 三 节
前端
小石潭记丶1 小时前
sqlalchemy查询json
java·前端·json
好_快3 小时前
Lodash源码阅读-baseMatchesProperty
前端·javascript·源码阅读