3D旋转 + SVG路径动画 + 左右按钮控制
3D旋转 + SVG路径动画 + 左右按钮控制
一、代码示例
<template> <div> <div class="container"> <!-- 3D场景 --> <div class="scene"> <div class="carousel" id="carousel"> <!-- 菜单项由JavaScript动态生成 --> <div ref="menuItem" v-for="item in menuItems" class="menu-item" @mouseover="fnMouseOver()" @mouseout="fnMouseOut()" @click="fnClickUp(item)"> {{ item.title }} </div> </div> </div> <!-- <div class="indicator"> 当前选中: <span id="currentIndex">{{ this.activeIndex }}</span>/<span id="totalItems"> {{ this.menuItems.length }}</span> </div> --> <!-- 控制按钮 --> <div class="controls"> <button class="control-btn" id="rightBtn" @click="rotateNext"> <i class="fas fa-chevron-right"></i> 下一个 </button> <button class="control-btn" id="leftBtn" @click="rotatePrev"> <i class="fas fa-chevron-left"></i> 上一个 </button> </div> </div> </div> </template> <script> export default { name: "index", data() { return { menuItems: [ { title: "就业完成指标情况分析", description: "描述" }, { title: "业务办理情况分析", description: "描述" }, { title: "登记失业人员分析", description: "描述" }, { title: "城镇新增人员就业分析", description: "描述" }, { title: "技能培训分析", description: "描述" }, { title: "职资帮扶情况分析", description: "描述" }, { title: "基本信息分析", description: "描述" }, { title: "农村劳动力转移就业情况", description: "描述" }, { title: "城镇就业就业情况", description: "描述" }, { title: "就业帮扶工作情况", description: "描述" }, { title: "站点招工信息环境", description: "描述" }, { title: "联合企业业人员情况", description: "描述" } ], currentRotation: 0, activeIndex: 0, rotationStep: 0, // 每个菜单项的角度(30度) rotationDirection: 'right', // 旋转方向(right,left) timer: null, // 定时器 isRunning: true, // 控制旋转状态 } }, mounted() { this.initCarousel(); this.startTimer() }, methods: { initCarousel() { this.rotationStep = 360 / this.menuItems.length // 旋转控制变量 this.updateMenuPositions(); }, updateMenuPositions () { const radius = 300; // 旋转半径 const count = this.menuItems.length; this.$refs.menuItem.forEach((item, index) => { // 计算角度(添加偏移量实现旋转) const angle = (index / count) * Math.PI * 2 + this.currentRotation; // 计算3D位置(上下位置调换) const x = Math.sin(angle) * radius + 50; const z = Math.cos(angle) * radius + 50; const y = Math.cos(angle) * 50; // 上下调换后的垂直偏移 // 计算与观察者的距离(用于远近效果) const distance = Math.sqrt(x*x + y*y + z*z); const maxDistance = Math.sqrt(radius*radius + radius*radius); // 根据距离计算缩放比例和透明度(近大远小,近实远虚) // const scale = 0.7 + 0.5 * (1 - distance / maxDistance); const scale = 0.7 - 0.5 * (1 - distance / maxDistance); const opacity = 0.5 + 0.5 * (1 - distance / maxDistance); // 应用变换 item.style.transform = ` translate3d(0, 0, 0) translateX(${x}px) translateY(${y}px) translateZ(${z}px) scale(${scale}) `; // 应用透明度 item.style.opacity = opacity; // 激活状态处理 if (index === this.activeIndex) { item.classList.add('active'); } else { item.classList.remove('active'); } }) }, // 旋转到指定索引 rotateToIndex(index) { // 计算目标旋转角度(使该索引位于前面) const targetRotation = -index * (Math.PI * 2 / this.menuItems.length); // 应用旋转 this.currentRotation = targetRotation; this.activeIndex = index; this.updateMenuPositions(); }, // 旋转到下一个项目 rotateNext() { this.activeIndex = (this.activeIndex + 1) % this.menuItems.length; this.rotateToIndex(this.activeIndex); }, // 旋转到上一个项目 rotatePrev() { this.activeIndex = (this.activeIndex - 1 + this.menuItems.length) % this.menuItems.length; this.rotateToIndex(this.activeIndex); }, fnMouseOver() { this.isRunning = false; }, fnMouseOut() { this.isRunning = true; }, fnClickUp (item) { }, startTimer() { this.timer = setInterval(() => { if(!this.isRunning) return if (this.rotationDirection === 'right') { this.rotatePrev(); } else { this.rotateNext(); } }, 2000); }, pauseTimer() { this.isRunning = false; clearInterval(this.timer); this.timer = null; } } } </script> <style scoped> .container { position: relative; top: 8rem; width: 100%; max-width: 1200px; text-align: center; padding: 20px; z-index: 10; } /* 3D场景容器 */ .scene { width: 500px; height: 300px; margin: 0 auto 50px; position: relative; transform-style: preserve-3d; } .carousel { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transform: rotateX(-10deg); /* 上下翻转 */ transition: transform 0.8s cubic-bezier(0.25, 0.1, 0.25, 1); } /* 菜单项样式 */ .menu-item { position: absolute; width: 100px; height: 80px; background: rgba(255, 255, 255, 0.1); border-radius: 15px; display: flex; flex-direction: column; justify-content: center; align-items: center; color: #000; font-weight: bold; box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3); cursor: pointer; transition: all 0.5s ease; transform-origin: center center; transform-style: preserve-3d; backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.15); overflow: hidden; text-align: center; padding: 15px; font-size: 16px; line-height: 1.4; } .menu-item::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 4px; background: linear-gradient(90deg, #457b9d, #a8dadc); } /* 悬停效果 */ .menu-item:hover { background: rgba(168, 218, 220, 0.2); box-shadow: 0 8px 25px rgba(69, 123, 157, 0.5); } /* 选中项效果 */ .menu-item.active { background: rgba(69, 123, 157, 0.3); box-shadow: 0 0 25px rgba(69, 123, 157, 0.6); border: 1px solid rgba(168, 218, 220, 0.5); transform: translateZ(20px) scale(1.05); } /* 控制按钮容器 */ .controls { display: flex; justify-content: center; gap: 20px; margin-top: 30px; flex-wrap: wrap; } /* 控制按钮样式 */ .control-btn { padding: 14px 30px; background: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 50px; color: #000; font-size: 1.05rem; cursor: pointer; transition: all 0.3s ease; backdrop-filter: blur(5px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); display: flex; align-items: center; gap: 10px; } .control-btn:hover { background: rgba(69, 123, 157, 0.25); transform: translateY(-3px); box-shadow: 0 8px 20px rgba(69, 123, 157, 0.3); } .control-btn:active { transform: translateY(1px); } /* 选中项信息面板 */ /* .selected-info { background: rgba(255, 255, 255, 0.05); border-radius: 20px; padding: 25px; margin-top: 40px; backdrop-filter: blur(8px); border: 1px solid rgba(255, 255, 255, 0.1); max-width: 800px; margin: 50px auto 0; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); min-height: 180px; display: flex; flex-direction: column; justify-content: center; align-items: center; } .selected-info h2 { color: #a8dadc; margin-bottom: 20px; display: flex; align-items: center; justify-content: center; gap: 12px; font-size: 1.8rem; } .selected-info p { color: rgba(255, 255, 255, 0.9); line-height: 1.7; font-size: 1.1rem; text-align: center; } */ /* .stats { display: flex; gap: 30px; margin-top: 20px; flex-wrap: wrap; justify-content: center; } .stat-item { background: rgba(255, 255, 255, 0.07); border-radius: 15px; padding: 15px 25px; min-width: 150px; border: 1px solid rgba(168, 218, 220, 0.2); } .stat-value { font-size: 2rem; font-weight: bold; color: #a8dadc; margin-bottom: 5px; } .stat-label { font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); } */ /* 响应式设计 */ /* @media (max-width: 768px) { .scene { width: 350px; height: 350px; } .menu-item { width: 130px; height: 100px; font-size: 14px; padding: 10px; } h1 { font-size: 2.2rem; } .controls { gap: 15px; } .control-btn { padding: 12px 20px; font-size: 0.95rem; } .selected-info h2 { font-size: 1.5rem; } .selected-info p { font-size: 1rem; } } */ /* 指示器 */ .indicator { margin-top: 20px; color: rgba(255, 255, 255, 0.7); font-size: 0.9rem; } </style>
二、效果图:

浙公网安备 33010602011771号