
Index.wxml
<!-- pages/index.wxml -->
<view class="container">
<view class="turntable">
<view
class="wheel"
style="transform: rotate({{rotateAngle}}deg); transition: transform {{duration}}s cubic-bezier(0.25, 0.1, 0.25, 1);">
<view
wx:for="{{prizes}}"
wx:key="index"
class="prize-item"
style="transform: rotate({{index * (360 / prizes.length)}}deg);">
<view class="prize-text">
<view class="prize-level">{{item.level}}</view>
<view class="prize-title">{{item.title}}</view>
</view>
</view>
</view>
<!-- 指针 -->
<view class="pointer">
<view class="pointer-arrow"></view>
</view>
</view>
<button class="spin-btn" bindtap="spin" disabled="{{isSpinning}}">开始抽奖</button>
</view>
Index.wxss
/* pages/index.wxss */
.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background: #f8f8f8;
}
.turntable {
position: relative;
width: 300px;
height: 300px;
margin-bottom: 40px;
.wheel {
width: 100%;
height: 100%;
border-radius: 50%;
background: conic-gradient(
#ff6b6b 0% 12.5%,
#4ecdc4 12.5% 25%,
#45b7d1 25% 37.5%,
#96ceb4 37.5% 50%,
#ffeead 50% 62.5%,
#ff9f43 62.5% 75%,
#f15bb5 75% 87.5%,
#00bbf9 87.5% 100%
);
position: relative;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
overflow: hidden;
}
.prize-item {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 50%;
transform-origin: 50% 100%;
}
.prize-text {
position: absolute;
bottom: 22%;
left: 50%;
transform: translateX(-50%) rotate(90deg);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 12px;
font-weight: bold;
color: white;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
width: 60px;
white-space: normal;
text-align: center;
padding: 8px 4px;
overflow: hidden;
}
.prize-level {
font-size: 14px;
line-height: 18px;
margin: 0;
}
.prize-title {
font-size: 12px;
line-height: 16px;
margin: 0;
}
.pointer {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 10;
width: 60px;
height: 60px;
background: #fff;
border-radius: 50%;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
display: flex;
justify-content: center;
align-items: center;
}
.pointer-arrow {
width: 0;
height: 0;
border-left: 15px solid transparent;
border-right: 15px solid transparent;
border-bottom: 30px solid #ff4757;
position: absolute;
top: -30px;
}
.spin-btn {
padding: 16px 40px;
font-size: 18px;
background: linear-gradient(135deg, #ff6b6b, #ee5253);
color: white;
border-radius: 25px;
border: none;
box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
}
.spin-btn:disabled {
background: #ccc;
box-shadow: none;
}
Index.js
// pages/index.js
Page({
data: {
// 奖品设置:8个奖品(每项分为等级 level 与具体标题 title)
prizes: [
{ level: "一等奖", title: "价值大礼", color: "#ff6b6b" },
{ level: "未中奖", title: "谢谢参与", color: "#4ecdc4" },
{ level: "二等奖", title: "精美礼品", color: "#45b7d1" },
{ level: "再来一次", title: "再来一次", color: "#96ceb4" },
{ level: "三等奖", title: "幸运奖", color: "#ffeead" },
{ level: "安慰奖", title: "小礼品", color: "#ff9f43" },
{ level: "特等奖", title: "超值大奖", color: "#f15bb5" },
{ level: "幸运奖", title: "神秘礼", color: "#00bbf9" }
],
rotateAngle: 0,
duration: 4,
isSpinning: false
},
spin() {
if (this.data.isSpinning) return;
this.setData({ isSpinning: true });
// 随机生成中奖结果(0-7)
const winningIndex = Math.floor(Math.random() * this.data.prizes.length);
// 计算需要旋转的角度(修正:按当前角度计算增量,避免重复计入 currentRotation)
// 基础圈数和每个奖品角度
const baseRotation = 360 * 5; // 至少旋转5圈
const itemAngle = 360 / this.data.prizes.length;
// 中奖项中心角度(未旋转时)
const targetCenter = winningIndex * itemAngle + itemAngle / 2;
// 当前角度模360
const currentRotation = this.data.rotateAngle;
const currentMod = ((currentRotation % 360) + 360) % 360;
// 需要增量使得 (targetCenter + currentMod + delta) %360 === 0
const offset = (360 - ((targetCenter + currentMod) % 360)) % 360;
const delta = baseRotation + offset;
const finalRotation = currentRotation + delta;
// 设置动画
this.setData({
rotateAngle: finalRotation,
duration: 4
});
// 动画结束后显示结果并进行诊断(以computedIndex为准,保证与视觉一致)
setTimeout(() => {
this.setData({ isSpinning: false });
// 诊断:计算最终角度并推断停在指针处的索引
const angleMod = ((finalRotation % 360) + 360) % 360;
let computedIndex = 0;
let minDiff = 360;
for (let i = 0; i < this.data.prizes.length; i++) {
const center = ((i * itemAngle + itemAngle / 2 + finalRotation) % 360 + 360) % 360;
const diff = Math.min(Math.abs(center - 0), Math.abs(center - 360));
if (diff < minDiff) { minDiff = diff; computedIndex = i; }
}
const prize = this.data.prizescomputedIndex;
const intended = this.data.prizeswinningIndex;
console.log('winningIndex', winningIndex, 'intendedPrize', `{intended.level} --- {intended.title}`, 'finalRotation', finalRotation, 'angleMod', angleMod);
console.log('computedIndex', computedIndex, 'computedPrize', `{prize.level} --- {prize.title}`);
console.log(`恭喜!你抽中了:{prize.level} --- {prize.title}`);
// 可选:添加成功提示(使用实际停位的奖项)
wx.showToast({
title: `抽中:{prize.level} --- {prize.title}`,
icon: 'success',
duration: 2000
});
}, 4000); // 与动画时长一致
}
});
