本文介紹一種12宮格抽獎,這種抽獎方式是九宮格抽獎的演變,只不過開始播放的時候是隨機方式,不是順時針播放了。
1.需求
先看看高保,如下圖1
圖1
- 用戶點擊"立即抽獎"后12個獎品隨機高亮
- 請求接口,待所有產品都高亮后,根據接口結果,固定在抽中獎品上
- 最后彈出中獎結果彈框
2.思路
2.1 高亮
實現圖片的高亮有很多種方式,上一篇九宮格抽獎中使用的是修改圖片容器的背景色,這里也可以讓UI同事涉及兩套圖片,一套是普通顯示的圖片,一套是高亮顯示的,這樣只要使用vue動態屬性來切換就可以了。
2.2 隨機閃爍
隨機閃爍需要用到隨機數組,可以先得到獎品數據的下標數組,然后隨機打亂這個數組得到混亂的下標,最后遍歷數組查找對應的下標,切換高亮屬性。
2.3 定時器
最后,要看到閃爍的結果,不能直接遍歷數組,需要一個定時器,我們這里依然使用定時任務+async的方式實現,具體方式在下面實現過程步驟中詳細介紹。
3.實現過程
3.1 獎品數據
按照高保給的順序定義獎品數組,注意數組設置一個active屬性,表示當前獎品是否高亮。代碼如下:
prizes: [
{active: false, prizeName: '華為智能手環4', imgName: '1-huawei'},
{active: false, prizeName: '米家聲波電動牙刷T300', imgName: '1-mijia'},
{active: false, prizeName: 'AX1800千兆雙頻路由器', imgName: '1-router'},
{active: false, prizeName: 'vivo TWS Neo耳機', imgName: '1-100m'},
{active: false, prizeName: 'OPPO w31真無線耳機', imgName: '1-oppo_w31'},
{active: false, prizeName: 'vivo Watch手表', imgName: '1-200m'},
{active: false, prizeName: 'reno4智能手機', imgName: '1-10y'},
{active: false, prizeName: '華為智能手表', imgName: '1-1g'},
{active: false, prizeName: 'vivo X5 Pro', imgName: '1-20g'},
{active: false, prizeName: 'vivo TWS Neo耳機', imgName: '1-vivo_neo'},
{active: false, prizeName: '小米10智能手機', imgName: '1-100y'},
{active: false, prizeName: 'vivo X50 Pro', imgName: '1-2g'}
]
3.2 高亮
這里UI提供了12個獎品圖片對應的高亮圖片,只要切換成高亮的圖片就可以了,這個使用vue動態屬性很容易實現。圖片風格由UI同事負責設計,在這里要區分當圖片的選中狀態,我們在原圖片名字后面加上后綴“_on”來區分選中圖片,如下圖2
圖2
3.3 隨機數組
有了上面的獎品數據,可以很容得到下標數組,然后要打斷這個數組,得到一個隨機數字數組。這里使用了洗牌算法來打亂數組,方法如下:
//隨機排列數,洗牌算法
shuffle(arr) {
let i = arr.length, t, j
while (i) {
j = Math.floor(Math.random() * (i--))
t = arr[i]
arr[i] = arr[j]
arr[j] = t
}
}
調用方式如下:
let arr = Array(this.prizes.length).fill(0).map((i, index) => index)
console.log("打亂前的數組:", arr)
//隨機打亂數組元素
this.shuffle(arr)
console.log("打亂后的數組", arr)
結果如下圖3
圖3
3.4 定時器&異步事件
這里使用一個定時器來執行延遲,定時器在一個延遲時間結束后再執行一個事件,這樣這個事件就是一個異步事件了。
然后使用async/await把異步任務串起來,使它們按照一定的順序來執行。代碼如下:
let arr = Array(this.prizes.length).fill(0).map((i, index) => index)
let timeoutID = null
//模擬定時器
let sleep = time => new Promise((resolve) => {
timeoutID = setTimeout(resolve, time)
})
//定時任務函數,async/await
let collectEvent = async(count, interval) => {
for (let i = 0; i < count; i++) {
//等待上一個任務完成
await sleep(interval)
//按照隨機數顯示高亮
this.prizes.forEach((p, index) => { p.active = index === arr[i] })
}
}
//產生隨機數組
this.shuffle(arr)
//加入定時任務
collectEvent(arr.length, 300).then(() => {
clearTimeout(timeoutID)
})
加入定時任務的時候,collectEven方法內有12個await語句,每個await后面又是一個異步任務,這樣執行collectEvent的時候會等待上一個await后面的語句有了返回再執行下一個await語句。sleep()方法會再規定時間(參數interval,傳值300)內執行resolve,這樣每次高亮都會間隔300毫秒時間,看上去是在隨機高亮。效果如下圖4
圖4
這里是隨機閃爍,如果要求按照一定的順序來閃爍,就需要按照順序構造數組arr了。
3.5 時間間隔
這里閃爍的間隔是300毫秒,如果需要由快到慢或者由慢到快的閃爍,就需要在sleep()上做些改動,例如我們可以給sleep()函數傳值的時候用一個表達式,使用Math.pow()來得到時間間隔,將調用sleep的語句修改一下
await sleep(interval)
改成
await sleep(interval * i)
然后調用的時候修改一下時間
collectEvent(arr.length, 300)
改成
collectEvent(arr.length, 100)
這樣每次執行異步任務的間隔時間會比上一次多100毫秒,來看下效果如下圖5
圖5
3.6 中獎彈框
最后執行完閃爍之后要根據接口返回來固定到某個獎品上並彈出中獎彈框。使用一個隨機選擇函數模擬抽獎接口,函數如下:
//生成隨機獎品
getRandomIntInclusive(min, max) {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min + 1)) + min
}
調用方式如下:
//加入定時任務
collectEvent(arr.length, 100).then(() => {
clearTimeout(timeoutID)
let checkedPrizeIndex = this.getRandomIntInclusive(0, this.prizes.length - 1)
this.prizes.forEach((p, index) => { p.active = index === checkedPrizeIndex })
this.prizeInfo = this.prizes[checkedPrizeIndex]
this.raffleResultShow = true
})
如何實現中獎彈框就不再贅述了,最后的效果如下圖6
圖6
4.總結
本文介紹了如何實現12宮格抽獎功能,主要用到的技術有隨機打亂數組(洗牌算法),定時任務,異步任務隊列等。