这是一个微信小程序九宫格抽奖页面的完整代码,包括 WXML、WXSS、JS 和 JSON。
效果
九宫格抽奖
功能说明:
- 静态页面布局: 3x3 九宫格,中间是"立即抽奖"按钮,周围是奖品金额。
- 抽奖动画: 点击"立即抽奖"后,九宫格会动态跑马灯式高亮显示,最终停留在中奖项。
- 中奖提示: 抽奖结束后弹出中奖结果。
1. lottery.json
(页面配置)
json
{
"navigationBarTitleText": "九宫格抽奖",
"usingComponents": {}
}
2. lottery.wxml
(页面结构)
xml
<view class="container">
<view class="lottery-grid">
<block wx:for="{{gridItems}}" wx:key="id">
<view
class="lottery-item {{item.isButton ? 'lottery-btn' : ''}} {{item.active ? 'active' : ''}}"
data-index="{{item.id}}"
bindtap="{{item.isButton ? 'startLottery' : ''}}"
>
<text>{{item.text}}</text>
</view>
</block>
</view>
</view>
3. lottery.wxss
(页面样式)
css
/* container */
.container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh; /* 撑满整个屏幕高度 */
background-color: #f8f8f8;
padding: 20rpx;
box-sizing: border-box;
}
/* 九宫格容器 */
.lottery-grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3列,每列等宽 */
gap: 15rpx; /* 格子之间的间距 */
width: 700rpx; /* 九宫格总宽度 */
background-color: #fff;
border-radius: 20rpx;
padding: 15rpx;
box-shadow: 0 5rpx 20rpx rgba(0, 0, 0, 0.1);
}
/* 抽奖格子公共样式 */
.lottery-item {
width: 100%; /* 充满父容器的宽度 */
height: 200rpx; /* 高度 */
display: flex;
justify-content: center;
align-items: center;
background-color: #fdfdfd;
border: 4rpx solid #ffd700; /* 金色边框 */
border-radius: 15rpx;
font-size: 36rpx;
font-weight: bold;
color: #333;
transition: all 0.1s ease; /* 动画过渡效果 */
box-sizing: border-box; /* 边框和内边距不撑大元素 */
}
/* 中间抽奖按钮样式 */
.lottery-btn {
background-color: #ffda47; /* 醒目的黄色 */
color: #d84e27; /* 红色字体 */
font-size: 40rpx;
border-color: #f77026; /* 橙色边框 */
box-shadow: 0 4rpx 8rpx rgba(255, 165, 0, 0.4);
cursor: pointer;
}
/* 激活(高亮)状态样式 */
.lottery-item.active {
background-color: #ff4d4f; /* 红色高亮 */
color: #fff;
border-color: #ff0000;
transform: scale(1.03); /* 稍微放大 */
box-shadow: 0 0 20rpx rgba(255, 77, 79, 0.8);
}
/* 按钮禁用状态(抽奖进行中) */
.lottery-btn[disabled] {
opacity: 0.7;
cursor: not-allowed;
background-color: #ccc;
border-color: #aaa;
}
4. lottery.js
(页面逻辑)
javascript
Page({
data: {
// 九宫格数据
// id: 格子索引 (0-8)
// text: 显示的文本
// isButton: 是否是抽奖按钮 (中间格子)
// active: 是否是当前高亮格子
gridItems: [],
// 抽奖相关状态
currentIndex: -1, // 当前高亮的格子索引
isDrawing: false, // 是否正在抽奖
timer: null, // 抽奖定时器
speed: 200, // 初始转动速度 (ms)
minSpeed: 50, // 最快转动速度 (ms)
drawCount: 0, // 已经转动的次数
totalRounds: 3, // 至少转动多少圈 (影响动画时长)
finalPrizeIndex: -1, // 最终中奖的格子索引
accelerateSteps: 10, // 加速阶段的步数
decelerateSteps: 10, // 减速阶段的步数
// 抽奖路径 (按顺时针方向,跳过中间按钮4)
// 0 1 2
// 7 X 3 (X是中间按钮4)
// 6 5 4
lotteryPath: [0, 1, 2, 5, 8, 7, 6, 3],
},
onLoad: function () {
this.initGridItems();
},
/**
* 初始化九宫格数据
*/
initGridItems: function () {
const initialGridData = [
{ id: 0, text: '5元' },
{ id: 1, text: '8元' },
{ id: 2, text: '10元' },
{ id: 3, text: '90元' }, // 注意这个位置,对应抽奖路径
{ id: 4, text: '立即抽奖', isButton: true }, // 中间按钮
{ id: 5, text: '20元' }, // 注意这个位置,对应抽奖路径
{ id: 6, text: '50元' },
{ id: 7, text: '40元' },
{ id: 8, text: '30元' }
];
const gridItems = initialGridData.map(item => ({
...item,
active: false,
isButton: item.isButton || false // 确保 isButton 属性存在
}));
this.setData({
gridItems: gridItems
});
},
/**
* 获取下一个高亮的格子索引
*/
getNextLotteryIndex: function (currentPathIndex) {
const { lotteryPath } = this.data;
return (currentPathIndex + 1) % lotteryPath.length;
},
/**
* 开始抽奖
*/
startLottery: function () {
if (this.data.isDrawing) {
wx.showToast({
title: '正在抽奖中...',
icon: 'none'
});
return;
}
this.setData({
isDrawing: true,
drawCount: 0,
speed: 200, // 恢复初始速度
});
// 清除上次的定时器,避免重复
if (this.data.timer) {
clearTimeout(this.data.timer);
}
// 模拟抽奖结果,这里排除中间按钮(index 4)
const prizePoolIndexes = this.data.lotteryPath; // 只有周围的8个格子能中奖
const randomIndexInPool = Math.floor(Math.random() * prizePoolIndexes.length);
const finalPrizeIndex = prizePoolIndexes[randomIndexInPool];
console.log("最终中奖格子索引 (在 lotteryPath 中的位置):", randomIndexInPool);
console.log("最终中奖格子在 gridItems 中的实际ID:", finalPrizeIndex);
this.setData({
finalPrizeIndex: finalPrizeIndex,
currentIndex: this.data.lotteryPath[0] // 初始从第一个格子开始
}, () => {
// 确保 finalPrizeIndex 设置后再启动动画
this.runLottery();
});
},
/**
* 运行抽奖动画
*/
runLottery: function () {
let { gridItems, currentIndex, drawCount, speed, minSpeed,
finalPrizeIndex, lotteryPath, totalRounds, accelerateSteps, decelerateSteps } = this.data;
// 清除上一个高亮
if (currentIndex !== -1) {
let oldGridItems = gridItems;
const oldIndexInPath = lotteryPath.indexOf(currentIndex);
oldGridItems[lotteryPath[oldIndexInPath]].active = false;
this.setData({ gridItems: oldGridItems });
}
// 计算下一个高亮索引
const currentPathIndex = lotteryPath.indexOf(currentIndex);
const nextPathIndex = this.getNextLotteryIndex(currentPathIndex);
const nextActualIndex = lotteryPath[nextPathIndex];
// 更新高亮
gridItems[nextActualIndex].active = true;
this.setData({
gridItems: gridItems,
currentIndex: nextActualIndex,
drawCount: drawCount + 1
});
// 计算总的转动步数,确保至少转totalRounds圈 + 停到中奖位置
// 假设 finalPrizeIndex 是在 lotteryPath 中的实际索引
const prizePathIndex = lotteryPath.indexOf(finalPrizeIndex);
const minRunSteps = lotteryPath.length * totalRounds + prizePathIndex + 1; // 至少转的步数
// 速度控制
let newSpeed = speed;
if (drawCount < accelerateSteps) { // 加速阶段
newSpeed = Math.max(minSpeed, speed - (speed - minSpeed) / accelerateSteps * drawCount);
} else if (drawCount >= minRunSteps - decelerateSteps && drawCount < minRunSteps) { // 减速阶段
newSpeed = Math.min(200, speed + (200 - minSpeed) / decelerateSteps);
} else if (drawCount >= minRunSteps) { // 减速到最终结果,确保最终速度不会太快
newSpeed = Math.min(newSpeed + 30, 400); // 逐渐变慢
}
// 检查是否停止
const isStop = drawCount >= minRunSteps && currentIndex === finalPrizeIndex;
if (isStop) {
clearTimeout(this.data.timer);
this.setData({
isDrawing: false,
timer: null
});
wx.showModal({
title: '恭喜!',
content: '您抽中了' + gridItems[finalPrizeIndex].text + '!',
showCancel: false,
confirmText: '确定'
});
} else {
this.setData({ speed: newSpeed });
this.data.timer = setTimeout(() => this.runLottery(), newSpeed);
}
},
onUnload: function() {
// 页面卸载时清除定时器,避免内存泄漏
if (this.data.timer) {
clearTimeout(this.data.timer);
this.setData({ timer: null });
}
}
});