瑶都万能墙
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

447 lines
11 KiB

<template>
<view class="page">
<navbar leftClick
color="#fff"
bgColor="#667eea"
@leftClick="$utils.navigateBack" />
<view class="turntable-container">
<!-- 头部标题 -->
<view class="header">
<text class="title">幸运大转盘</text>
<text class="subtitle">转一转好运来</text>
</view>
<!-- 转盘区域 -->
<view class="turntable-wrapper">
<view class="turntable" :class="{ 'spinning': isSpinning }" :style="{ transform: `rotate(${rotateAngle}deg)` }">
<!-- 使用纯CSS创建转盘 -->
<view class="wheel-bg">
<!-- 8个扇形区域 -->
<view
v-for="(prize, index) in prizes"
:key="index"
class="wheel-sector"
:style="{
backgroundColor: sectorColors[index],
transform: `rotate(${index * 45}deg)`
}"
>
</view>
</view>
<!-- 奖品文字覆盖层 -->
<view class="prizes-overlay">
<view
v-for="(prize, index) in prizes"
:key="index"
class="prize-item"
:style="prizeStyles[index]"
>
<view class="prize-content">
<text class="prize-icon">{{ prize.icon }}</text>
<text class="prize-name">{{ prize.name }}</text>
<text class="prize-value">{{ prize.value }}</text>
</view>
</view>
</view>
</view>
<!-- 中心指针 -->
<view class="pointer">
<view class="pointer-triangle"></view>
</view>
<!-- 中心按钮 -->
<view class="center-button" @click="startSpin" :class="{ disabled: isSpinning }">
<text class="button-text">{{ isSpinning ? '抽奖中...' : '开始抽奖' }}</text>
</view>
</view>
<!-- 抽奖结果弹窗 -->
<view class="result-modal" v-if="showResult" @click="closeResult">
<view class="modal-content" @click.stop>
<text class="result-title">🎉 恭喜您 🎉</text>
<view class="result-prize">
<text class="result-icon">{{ currentPrize.icon }}</text>
<text class="result-name">{{ currentPrize.name }}</text>
<text class="result-value">{{ currentPrize.value }}</text>
</view>
<view class="result-actions">
<button class="confirm-btn" @click="closeResult">确定</button>
</view>
</view>
</view>
<!-- 抽奖次数提示 -->
<view class="spin-info">
<text>今日剩余抽奖次数: {{ remainingSpins }}</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 奖品配置
prizes: [
{ name: '现金奖励', value: '¥10', icon: '💰', type: 'money', amount: 10 },
{ name: '精美礼品', value: '小熊', icon: '🧸', type: 'gift', item: 'teddy_bear' },
{ name: '现金奖励', value: '¥5', icon: '💰', type: 'money', amount: 5 },
{ name: '精美礼品', value: '花束', icon: '💐', type: 'gift', item: 'flowers' },
{ name: '现金奖励', value: '¥20', icon: '💰', type: 'money', amount: 20 },
{ name: '精美礼品', value: '巧克力', icon: '🍫', type: 'gift', item: 'chocolate' },
{ name: '现金奖励', value: '¥2', icon: '💰', type: 'money', amount: 2 },
{ name: '精美礼品', value: '香水', icon: '🌸', type: 'gift', item: 'perfume' }
],
sectorColors: [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
'#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F'
],
isSpinning: false, // 是否正在旋转
rotateAngle: 0, // 旋转角度
showResult: false, // 显示结果弹窗
currentPrize: null, // 当前中奖奖品
remainingSpins: 3, // 剩余抽奖次数
sectorAngle: 45, // 每个扇形的角度 (360/8)
// 预计算奖品位置样式
prizeStyles: []
}
},
onLoad() {
this.calculatePrizeStyles()
},
methods: {
// 计算奖品位置样式
calculatePrizeStyles() {
this.prizeStyles = this.prizes.map((prize, index) => {
// 计算奖品在圆形中的位置
const angle = (index * 45 + 22.5) * Math.PI / 180; // 转换为弧度,22.5是偏移到扇形中心
const radius = 150; // 奖品距离中心的距离
const x = Math.cos(angle - Math.PI/2) * radius; // 减去90度,因为我们希望0度在顶部
const y = Math.sin(angle - Math.PI/2) * radius;
return `left: calc(50% + ${x}rpx); top: calc(50% + ${y}rpx); transform: translate(-50%, -50%);`
})
},
// 开始抽奖
startSpin() {
if (this.isSpinning || this.remainingSpins <= 0) {
if (this.remainingSpins <= 0) {
uni.showToast({
title: '今日抽奖次数已用完',
icon: 'none'
})
}
return
}
this.isSpinning = true
this.remainingSpins--
// 随机选择奖品
const prizeIndex = Math.floor(Math.random() * this.prizes.length)
this.currentPrize = this.prizes[prizeIndex]
// 计算旋转角度
// 每个扇形45度,指针指向扇形中心需要额外旋转22.5度
const targetAngle = 360 - (prizeIndex * this.sectorAngle + this.sectorAngle / 2)
const spinRounds = 5 // 转5圈
const finalAngle = this.rotateAngle + spinRounds * 360 + targetAngle
this.rotateAngle = finalAngle
// 动画结束后显示结果
setTimeout(() => {
this.isSpinning = false
this.showResult = true
this.handlePrizeResult()
}, 3000)
},
// 处理中奖结果
handlePrizeResult() {
if (this.currentPrize.type === 'money') {
// 处理现金奖励
console.log(`获得现金奖励: ${this.currentPrize.amount}元`)
// 这里可以调用API增加用户余额
} else if (this.currentPrize.type === 'gift') {
// 处理礼品奖励
console.log(`获得礼品: ${this.currentPrize.item}`)
// 这里可以调用API添加礼品到用户背包
}
},
// 关闭结果弹窗
closeResult() {
this.showResult = false
}
}
}
</script>
<style scoped lang="scss">
.turntable-container {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.header {
text-align: center;
margin-bottom: 60rpx;
color: white;
.title {
font-size: 48rpx;
font-weight: bold;
display: block;
margin-bottom: 20rpx;
}
.subtitle {
font-size: 28rpx;
opacity: 0.9;
}
}
.turntable-wrapper {
position: relative;
width: 600rpx;
height: 600rpx;
margin-bottom: 60rpx;
}
.turntable {
width: 100%;
height: 100%;
border-radius: 50%;
position: relative;
transition: transform 3s cubic-bezier(0.23, 1, 0.32, 1);
box-shadow: 0 0 40rpx rgba(0, 0, 0, 0.3);
overflow: hidden;
&.spinning {
transition-duration: 3s;
}
}
.wheel-bg {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
overflow: hidden;
}
.wheel-sector {
position: absolute;
width: 50%;
height: 50%;
top: 0%;
left: 50%;
transform-origin: 0% 100%;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: inherit;
clip-path: polygon(0% 100%, 50% 0%, 100% 100%);
}
// 添加边框线
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border-right: 2rpx solid rgba(255,255,255,0.3);
transform-origin: 0% 100%;
}
}
.prizes-overlay {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 990;
}
.prize-item {
position: absolute;
width: 120rpx;
height: 80rpx;
z-index: 10;
}
.prize-content {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: white;
text-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.8);
.prize-icon {
font-size: 32rpx;
display: block;
margin-bottom: 4rpx;
}
.prize-name {
font-size: 18rpx;
display: block;
font-weight: bold;
margin-bottom: 2rpx;
line-height: 1.2;
}
.prize-value {
font-size: 20rpx;
display: block;
font-weight: bold;
line-height: 1.2;
}
}
.pointer {
position: absolute;
top: -20rpx;
left: 50%;
transform: translateX(-50%);
z-index: 100;
.pointer-triangle {
width: 0;
height: 0;
border-left: 20rpx solid transparent;
border-right: 20rpx solid transparent;
border-top: 60rpx solid #FF4757;
filter: drop-shadow(0 4rpx 8rpx rgba(0, 0, 0, 0.3));
}
}
.center-button {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 160rpx;
height: 160rpx;
border-radius: 50%;
background: linear-gradient(145deg, #FF6B6B, #FF4757);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 20rpx rgba(255, 71, 87, 0.4);
z-index: 50;
transition: transform 0.2s;
&:active:not(.disabled) {
transform: translate(-50%, -50%) scale(0.95);
}
&.disabled {
opacity: 0.6;
cursor: not-allowed;
}
.button-text {
color: white;
font-size: 24rpx;
font-weight: bold;
text-align: center;
}
}
.result-modal {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.7);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
.modal-content {
background: white;
border-radius: 20rpx;
padding: 60rpx 40rpx;
text-align: center;
box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.3);
min-width: 500rpx;
.result-title {
font-size: 36rpx;
font-weight: bold;
color: #FF6B6B;
margin-bottom: 40rpx;
}
.result-prize {
margin-bottom: 40rpx;
.result-icon {
font-size: 60rpx;
display: block;
margin-bottom: 20rpx;
}
.result-name {
font-size: 32rpx;
color: #333;
display: block;
margin-bottom: 10rpx;
}
.result-value {
font-size: 36rpx;
color: #FF6B6B;
font-weight: bold;
}
}
.confirm-btn {
background: linear-gradient(45deg, #FF6B6B, #FF4757);
color: white;
border: none;
border-radius: 40rpx;
padding: 20rpx 60rpx;
font-size: 28rpx;
font-weight: bold;
}
}
}
.spin-info {
text-align: center;
color: white;
font-size: 28rpx;
background: rgba(255, 255, 255, 0.2);
padding: 20rpx 40rpx;
border-radius: 40rpx;
backdrop-filter: blur(10rpx);
}
</style>