這是一個微信小程序九宮格抽獎頁面的完整代碼,包括 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 });}}
});