-
BINsrc/assets/img/anniversary/-188.png
-
BINsrc/assets/img/anniversary/-27.png
-
BINsrc/assets/img/anniversary/-880.png
-
BINsrc/assets/img/anniversary/100.png
-
BINsrc/assets/img/anniversary/18.png
-
BINsrc/assets/img/anniversary/500.png
-
BINsrc/assets/img/anniversary/anniversary.png
-
BINsrc/assets/img/anniversary/guang.png
-
BINsrc/assets/img/anniversary/jiantou.png
-
BINsrc/assets/img/anniversary/pan.png
-
BINsrc/assets/img/anniversary/pandi.png
-
BINsrc/assets/img/anniversary/tanchuang.png
-
BINsrc/assets/img/anniversary/tanchuang1.png
-
7src/router/index.js
-
437src/views/platform/anniversary.vue
After Width: 96 | Height: 65 | Size: 6.8 KiB |
After Width: 96 | Height: 65 | Size: 6.6 KiB |
After Width: 96 | Height: 65 | Size: 5.0 KiB |
After Width: 96 | Height: 65 | Size: 6.3 KiB |
After Width: 96 | Height: 65 | Size: 5.5 KiB |
After Width: 96 | Height: 65 | Size: 5.9 KiB |
After Width: 1207 | Height: 679 | Size: 766 KiB |
After Width: 253 | Height: 253 | Size: 60 KiB |
After Width: 72 | Height: 90 | Size: 8.3 KiB |
After Width: 310 | Height: 310 | Size: 58 KiB |
After Width: 426 | Height: 492 | Size: 120 KiB |
After Width: 300 | Height: 300 | Size: 278 KiB |
After Width: 693 | Height: 736 | Size: 342 KiB |
@ -0,0 +1,437 @@ |
|||
<template> |
|||
<div class="container" :style="{ backgroundImage: `url(${anniversary})` }"> |
|||
<div class="turntable-container"> |
|||
<!-- 余额显示区域 --> |
|||
<div class="balance">{{ balance }}</div> |
|||
|
|||
<!-- 光效背景 --> |
|||
<div class="guang" :style="{ |
|||
backgroundImage: `url(${guang})`, |
|||
transform: `translate(-50%, -50%) rotate(${guangRotation}deg)` |
|||
}"></div> |
|||
|
|||
<!-- 转盘底座 --> |
|||
<div class="pandi" :style="{ backgroundImage: `url(${pandi})` }"></div> |
|||
|
|||
<!-- 转盘主体 --> |
|||
<div |
|||
class="pan" |
|||
:style="{ |
|||
transform: `translate(-50%, -50%) scale(1.5) rotate(${rotation}deg)`, |
|||
backgroundImage: `url(${pan})` |
|||
}" |
|||
> |
|||
<!-- 奖品位置 --> |
|||
<div |
|||
v-for="(prize, index) in prizePositions" |
|||
:key="index" |
|||
class="prize-container" |
|||
:style="getPrizeContainerStyle(index, rotation)" |
|||
> |
|||
<!-- 奖品图片,使用修改后的大小 --> |
|||
<img :src="getPrizeImage(prize.value)" class="prize-image"/> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 开始按钮 --> |
|||
<div |
|||
class="jiantou" |
|||
@click="startSpin" |
|||
:class="{ disabled: isSpinning || drawCount >= 8 }" |
|||
:style="{ backgroundImage: `url(${jiantou})` }" |
|||
></div> |
|||
|
|||
<!-- 中奖结果弹窗 --> |
|||
<transition name="flip"> |
|||
<div class="result-popup" v-if="showResult"> |
|||
<div class="popup-overlay"></div> |
|||
<div class="popup-content"> |
|||
<div class="popup-bg" :style="{ backgroundImage: `url(${tanchuang})` }"> |
|||
<!-- 添加了脉冲动画效果的中奖金额 --> |
|||
<div class="prize-value" :class="{ 'pulse-animation': showResult }">{{ resultValue }}</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</transition> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { ref, watch, onUnmounted } from 'vue' |
|||
|
|||
// ========== 静态资源路径 ========== |
|||
// 背景图 |
|||
const anniversary = new URL('@/assets/img/anniversary/anniversary.png', import.meta.url).href |
|||
// 转盘底座图 |
|||
const pandi = new URL('@/assets/img/anniversary/pandi.png', import.meta.url).href |
|||
// 转盘主体图 |
|||
const pan = new URL('@/assets/img/anniversary/pan.png', import.meta.url).href |
|||
// 开始按钮图 |
|||
const jiantou = new URL('@/assets/img/anniversary/jiantou.png', import.meta.url).href |
|||
// 弹窗背景图 |
|||
const tanchuang = new URL('@/assets/img/anniversary/tanchuang1.png', import.meta.url).href |
|||
// 光效图 |
|||
const guang = new URL('@/assets/img/anniversary/guang.png', import.meta.url).href |
|||
|
|||
// ========== 奖品配置 ========== |
|||
// 定义奖品位置和对应的角度 |
|||
const prizePositions = [ |
|||
{ value: '500', angle: 0 }, // 12点方向 |
|||
{ value: '-880', angle: 60 }, // 2点方向 |
|||
{ value: '18', angle: 120 }, // 4点方向 |
|||
{ value: '-27', angle: 180 }, // 6点方向 |
|||
{ value: '100', angle: 240 }, // 8点方向 |
|||
{ value: '-188', angle: 300 } // 10点方向 |
|||
] |
|||
|
|||
// ========== 状态管理 ========== |
|||
const balance = ref(8000) // 用户余额 |
|||
const rotation = ref(0) // 转盘当前旋转角度 |
|||
const guangRotation = ref(0) // 光效旋转角度 |
|||
const isSpinning = ref(false) // 是否正在旋转 |
|||
const showResult = ref(false) // 是否显示结果弹窗 |
|||
const resultValue = ref('') // 中奖结果值 |
|||
const drawCount = ref(0) // 已抽奖次数 |
|||
|
|||
// ========== 奖品概率分布 ========== |
|||
// 控制各奖项的中奖概率 |
|||
const prizeDistribution = ref([ |
|||
'-27', '-27', '-27', '-27', // -27元概率最高 |
|||
'18', '18', // 18元中等概率 |
|||
'100', // 100元低概率 |
|||
'-188' // -188元最低概率 |
|||
]) |
|||
|
|||
// ========== 奖品图片映射 ========== |
|||
const prizeImages = { |
|||
'18': new URL('@/assets/img/anniversary/18.png', import.meta.url).href, |
|||
'-27': new URL('@/assets/img/anniversary/-27.png', import.meta.url).href, |
|||
'100': new URL('@/assets/img/anniversary/100.png', import.meta.url).href, |
|||
'-188': new URL('@/assets/img/anniversary/-188.png', import.meta.url).href, |
|||
'500': new URL('@/assets/img/anniversary/500.png', import.meta.url).href, |
|||
'-880': new URL('@/assets/img/anniversary/-880.png', import.meta.url).href |
|||
} |
|||
|
|||
// ========== 光效旋转控制 ========== |
|||
let guangRotationInterval = null // 光效旋转定时器 |
|||
|
|||
// 监听弹窗显示状态变化 |
|||
watch(showResult, (newVal) => { |
|||
if (!newVal && drawCount.value < 8) { |
|||
startGuangRotation() // 关闭弹窗且抽奖未满8次时启动光效 |
|||
} else { |
|||
stopGuangRotation() // 否则停止光效 |
|||
} |
|||
}) |
|||
|
|||
// 组件卸载时清除定时器 |
|||
onUnmounted(() => { |
|||
stopGuangRotation() |
|||
}) |
|||
|
|||
// 启动光效旋转 |
|||
const startGuangRotation = () => { |
|||
if (guangRotationInterval) return |
|||
guangRotationInterval = setInterval(() => { |
|||
guangRotation.value = (guangRotation.value + 2) % 360 // 每10ms旋转2度 |
|||
}, 5) |
|||
} |
|||
|
|||
// 停止光效旋转 |
|||
const stopGuangRotation = () => { |
|||
if (guangRotationInterval) { |
|||
clearInterval(guangRotationInterval) |
|||
guangRotationInterval = null |
|||
} |
|||
} |
|||
|
|||
// 获取奖品图片 |
|||
const getPrizeImage = (prize) => { |
|||
return prizeImages[prize] || '' |
|||
} |
|||
|
|||
// 计算奖品容器样式 |
|||
const getPrizeContainerStyle = (index, currentRotation) => { |
|||
const prizeAngle = prizePositions[index].angle // 奖品原始角度 |
|||
const radius = 100 // 奖品距离中心的半径 |
|||
const radian = prizeAngle * Math.PI / 180 // 转换为弧度 |
|||
// 计算x,y坐标 |
|||
const x = Math.sin(radian) * radius |
|||
const y = -Math.cos(radian) * radius |
|||
// 补偿旋转,使奖品始终正向显示 |
|||
const compensateRotation = -currentRotation |
|||
|
|||
return { |
|||
position: 'absolute', |
|||
left: '50%', |
|||
top: '50%', |
|||
width: '80px', |
|||
height: '80px', |
|||
marginLeft: '-40px', // 居中定位 |
|||
marginTop: '-40px', // 居中定位 |
|||
transform: `translate(${x}px, ${y}px) rotate(${compensateRotation}deg)`, |
|||
transformOrigin: 'center center', |
|||
display: 'flex', |
|||
justifyContent: 'center', |
|||
alignItems: 'center', |
|||
transition: 'transform 5s cubic-bezier(0.17, 0.67, 0.21, 0.99)' // 平滑旋转过渡 |
|||
} |
|||
} |
|||
|
|||
// 开始旋转转盘 |
|||
const startSpin = () => { |
|||
// 如果正在旋转或已抽奖8次,则不允许再次抽奖 |
|||
if (isSpinning.value || drawCount.value >= 8) return |
|||
|
|||
isSpinning.value = true // 设置旋转状态 |
|||
showResult.value = false // 隐藏结果弹窗 |
|||
stopGuangRotation() // 停止光效 |
|||
|
|||
// 随机选择一个奖品 |
|||
const randomIndex = Math.floor(Math.random() * prizeDistribution.value.length) |
|||
const selectedPrize = prizeDistribution.value[randomIndex] |
|||
// 从奖品池中移除已中奖项 |
|||
prizeDistribution.value.splice(randomIndex, 1) |
|||
|
|||
// 查找选中的奖品位置信息 |
|||
const targetPrize = prizePositions.find(p => p.value === selectedPrize) |
|||
if (!targetPrize) { |
|||
isSpinning.value = false |
|||
return |
|||
} |
|||
|
|||
// 计算需要旋转的角度 |
|||
const currentAngle = rotation.value % 360 |
|||
let remainingAngle = (720 - currentAngle - targetPrize.angle) % 360 |
|||
// 总共旋转4圈(1440度)加上剩余角度 |
|||
const totalRotation = rotation.value + 1440 + remainingAngle |
|||
|
|||
// 设置旋转角度 |
|||
rotation.value = totalRotation |
|||
|
|||
// 5秒后停止旋转并显示结果 |
|||
setTimeout(() => { |
|||
isSpinning.value = false |
|||
drawCount.value++ |
|||
resultValue.value = selectedPrize |
|||
showResult.value = true |
|||
|
|||
// 2秒后隐藏结果弹窗并更新余额 |
|||
setTimeout(() => { |
|||
showResult.value = false |
|||
balance.value += parseInt(selectedPrize) |
|||
}, 2500) |
|||
}, 5000) |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
/* 容器样式 */ |
|||
.container { |
|||
width: 100vw; |
|||
height: 100vh; |
|||
background-image: url(${anniversary}); |
|||
background-size: 100% auto; /* 背景图宽度100%,高度自适应 */ |
|||
background-position: center center; |
|||
background-repeat: no-repeat; |
|||
background-color: #000; /* 黑色背景 */ |
|||
overflow: hidden; |
|||
position: relative; |
|||
} |
|||
|
|||
/* 转盘容器 */ |
|||
.turntable-container { |
|||
position: relative; |
|||
width: 500px; |
|||
height: 500px; |
|||
margin: auto auto 10px; /* 居中 */ |
|||
} |
|||
|
|||
/* 余额显示样式 */ |
|||
.balance { |
|||
position: absolute; |
|||
top: -1%; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
font-size: 120px; |
|||
text-shadow: 0 0 20px rgba(255, 215, 0, 0.5); /* 金色发光效果 */ |
|||
font-weight: 900; |
|||
color: rgb(255,214,66); /* 金色文字 */ |
|||
z-index: 1000; |
|||
width: 150%; |
|||
text-align: center; |
|||
font-family: 'Arial Black', Gadget, sans-serif; |
|||
letter-spacing: 2px; |
|||
} |
|||
|
|||
/* 光效样式 */ |
|||
.guang { |
|||
position: absolute; |
|||
top: 15%; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
width: 500px; |
|||
height: 400px; |
|||
background-size: contain; |
|||
background-position: center; |
|||
background-repeat: no-repeat; |
|||
z-index: 1; |
|||
transform-origin: center center; /* 旋转中心 */ |
|||
} |
|||
|
|||
/* 转盘底座样式 */ |
|||
.pandi { |
|||
position: absolute; |
|||
width: 90%; |
|||
height: 90%; |
|||
left: 50%; |
|||
transform: translateX(-50%) scale(1.8); /* 放大1.8倍 */ |
|||
bottom: -65%; |
|||
background-size: contain; |
|||
background-position: center bottom; |
|||
background-repeat: no-repeat; |
|||
z-index: 2; |
|||
} |
|||
|
|||
/* 转盘主体样式 */ |
|||
.pan { |
|||
position: absolute; |
|||
width: 67%; |
|||
height: 67%; |
|||
bottom: -78.5%; |
|||
left: 48%; |
|||
transform-origin: center center; /* 旋转中心 */ |
|||
background-size: contain; |
|||
background-position: center; |
|||
background-repeat: no-repeat; |
|||
z-index: 3; |
|||
transition: transform 5s cubic-bezier(0.17, 0.67, 0.21, 0.99); /* 平滑旋转动画 */ |
|||
} |
|||
|
|||
/* 奖品容器样式 */ |
|||
.prize-container { |
|||
pointer-events: none; /* 禁止鼠标事件 */ |
|||
} |
|||
|
|||
/* 奖品图片样式 - 修改为更大尺寸 */ |
|||
.prize-image { |
|||
width: auto; |
|||
height: 70px; /* 从60px增大到70px */ |
|||
max-width: 90px; /* 从80px增大到90px */ |
|||
object-fit: contain; /* 保持纵横比 */ |
|||
} |
|||
|
|||
/* 开始按钮样式 */ |
|||
.jiantou { |
|||
position: absolute; |
|||
width: 15%; |
|||
height: 15%; |
|||
bottom: -25.4%; |
|||
left: 48%; |
|||
transform: translate(-50%, -50%) scale(1.5); /* 放大1.5倍 */ |
|||
background-size: contain; |
|||
background-position: center; |
|||
background-repeat: no-repeat; |
|||
z-index: 5; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
/* 禁用状态的开始按钮 */ |
|||
.jiantou.disabled { |
|||
cursor: not-allowed; |
|||
opacity: 1; /* 修改为完全不透明 */ |
|||
filter: grayscale(3 0%); /* 灰度效果表示禁用 */ |
|||
} |
|||
|
|||
/* 结果弹窗样式 */ |
|||
.result-popup { |
|||
position: fixed; |
|||
top: -7%; |
|||
left: -1.5%; |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: flex-start; |
|||
z-index: 100; |
|||
} |
|||
|
|||
/* 弹窗内容 */ |
|||
.popup-content { |
|||
position: relative; |
|||
z-index: 101; |
|||
} |
|||
|
|||
/* 弹窗背景 */ |
|||
.popup-bg { |
|||
width: 900px; |
|||
height: 900px; |
|||
background-size: contain; |
|||
background-position: center; |
|||
background-repeat: no-repeat; |
|||
display: flex; |
|||
justify-content: center; |
|||
top: 80%; |
|||
} |
|||
|
|||
/* 奖品值样式 */ |
|||
.prize-value { |
|||
font-size: 120px; |
|||
color: rgb(255,214,66); /* 金色文字 */ |
|||
font-weight: 900; |
|||
text-shadow: 0 0 20px rgba(255, 215, 0, 0.5); /* 发光效果 */ |
|||
margin-top: 230px; |
|||
font-family: 'Arial Black', Gadget, sans-serif; |
|||
} |
|||
|
|||
/* 添加的脉冲动画效果 */ |
|||
.pulse-animation { |
|||
animation: pulse 1s infinite alternate; /* 无限循环交替动画 */ |
|||
} |
|||
|
|||
/* 脉冲动画关键帧 */ |
|||
@keyframes pulse { |
|||
0% { |
|||
transform: scale(1.1); /* 原始大小 */ |
|||
text-shadow: 0 0 20px rgba(255, 215, 0, 0.5); |
|||
} |
|||
100% { |
|||
transform: scale(1); /* 放大10% */ |
|||
text-shadow: 0 0 30px rgba(255, 215, 0, 0.8); /* 更强的发光效果 */ |
|||
} |
|||
} |
|||
|
|||
/* 弹窗翻转动画 */ |
|||
.flip-enter-active { |
|||
animation: flipIn 0.01s ease-out; |
|||
} |
|||
|
|||
.flip-leave-active { |
|||
animation: flipOut 0.1s ease-in; |
|||
} |
|||
|
|||
/* 弹窗进入动画 */ |
|||
@keyframes flipIn { |
|||
0% { |
|||
transform: perspective(600px) rotateY(30deg); /* 从侧面翻转 */ |
|||
opacity: 0; |
|||
} |
|||
100% { |
|||
transform: perspective(600px) rotateY(0deg); /* 转到正面 */ |
|||
opacity: 1; |
|||
} |
|||
} |
|||
|
|||
/* 弹窗退出动画 */ |
|||
@keyframes flipOut { |
|||
0% { |
|||
transform: perspective(600px) rotateY(0deg); /* 从正面开始 */ |
|||
opacity: 1; |
|||
} |
|||
100% { |
|||
transform: perspective(600px) rotateY(60deg); /* 转到侧面 */ |
|||
opacity: 0; |
|||
} |
|||
} |
|||
</style> |