这是一个微信小程序九宫格抽奖页面的完整代码,包括 WXML、WXSS、JS 和 JSON。
效果
九宫格抽奖
功能说明:
- 静态页面布局: 3x3 九宫格,中间是“立即抽奖”按钮,周围是奖品金额。
- 抽奖动画: 点击“立即抽奖”后,九宫格会动态跑马灯式高亮显示,最终停留在中奖项。
- 中奖提示: 抽奖结束后弹出中奖结果。
1. lottery.json
(页面配置)
{"navigationBarTitleText": "九宫格抽奖","usingComponents": {}
}
2. lottery.wxml
(页面结构)
<view class="container"><view class="lottery-grid"><block wx:for="{{gridItems}}" wx:key="id"><viewclass="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
(页面样式)
/* 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
(页面逻辑)
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 4lotteryPath: [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 });}}
});