Vue3實(shí)現(xiàn)九宮格抽獎(jiǎng)效果的示例詳解
前言
好久沒(méi)有寫(xiě)文章了,上一次發(fā)文還是年終總結(jié),眨眼間又是一年,每每想多總結(jié)卻是堅(jiān)持不來(lái),難頂。 這次分享一個(gè)九宮格抽獎(jiǎng)小游戲,緣起是最近公司內(nèi)部做積分抽獎(jiǎng)需求,抽出其中抽獎(jiǎng)動(dòng)效做一個(gè)總結(jié),從零實(shí)現(xiàn)一個(gè)抽獎(jiǎng)功能的過(guò)程和注意點(diǎn)。
Demo功能代碼是使用vue3實(shí)現(xiàn)的,主要用到了組合式API,由于只是一個(gè)簡(jiǎn)單的demo就沒(méi)有使用腳手架和打包工具。
需求與效果
需求: 1、禮品根據(jù)后臺(tái)配置生成 2、跑馬燈轉(zhuǎn)動(dòng)效果 3、結(jié)果后臺(tái)生成并且每個(gè)禮物概率不一樣(概率這里不討論)
注意點(diǎn): 1、布局如何排列,是按照跑動(dòng)排列還是從左至右自上而下排列 2、點(diǎn)擊按鈕如何插入,DOM結(jié)構(gòu)如何生成 3、跑馬效果如何實(shí)現(xiàn),速度如何控制 4、接口如何處理,包括接口報(bào)錯(cuò)、請(qǐng)求Pending時(shí)特效 5、后臺(tái)返回結(jié)果和跑馬完選中結(jié)果一致等
最終效果圖:

注:圖片都是百度圖片找的
功能實(shí)現(xiàn)
第一步:實(shí)現(xiàn)布局
思路:請(qǐng)求到后臺(tái)配置的禮物列表,一般是配置了8個(gè)禮物,包括謝謝參與,第一種是手動(dòng)布局寫(xiě)9個(gè)禮物div標(biāo)簽,這種方式開(kāi)始按鈕可以直接寫(xiě)到布局里面。第二種可以遍歷,這里我使用遍歷的方式,但是按鈕就需要我們插入到禮物列表數(shù)組中去。css代碼可以使用flex布局,三行三列剛好,然后添加所需樣式。js部分主要是使用splice()方法插入<開(kāi)始按鈕>。
部分代碼:
<body>
<div id="app" v-cloak>
<div class="container">
<div :class="['item', {'active': currentIndex === index}]"
v-for="(item, index) in prizeList"
@click="start(index)">
<img :src="item.pic" alt="">
<p v-if="index !== 4">{{ item.name }}</p>
</div>
</div>
</div>
</body>const state = reactive({
prizeList: [
{ name: '手機(jī)', pic: 'https://bkimg.cdn.bcebos.com/pic/3801213fb80e7bec54e7d237ad7eae389b504ec23d9e' },
{ name: '手表', pic: 'https://img1.baidu.com/it/u=2631716577,1296460670&fm=253&fmt=auto&app=120&f=JPEG' },
{ name: '蘋(píng)果', pic: 'https://img2.baidu.com/it/u=2611478896,137965957&fm=253&fmt=auto&app=138&f=JPEG' },
{ name: '棒棒糖', pic: 'https://img2.baidu.com/it/u=576980037,1655121105&fm=253&fmt=auto&app=138&f=PNG' },
{ name: '娃娃', pic: 'https://img2.baidu.com/it/u=4075390137,3967712457&fm=253&fmt=auto&app=138&f=PNG' },
{ name: '木馬', pic: 'https://img1.baidu.com/it/u=2434318933,2727681086&fm=253&fmt=auto&app=120&f=JPEG' },
{ name: '德芙', pic: 'https://img0.baidu.com/it/u=1378564582,2397555841&fm=253&fmt=auto&app=120&f=JPEG' },
{ name: '玫瑰', pic: 'https://img1.baidu.com/it/u=1125656938,422247900&fm=253&fmt=auto&app=120&f=JPEG' }
], // 后臺(tái)配置的獎(jiǎng)品數(shù)據(jù)
})
const startBtn = { name: '開(kāi)始按鈕', pic: 'https://img2.baidu.com/it/u=1497996119,382735686&fm=253' }
onMounted(() => {
// state.prizeList.forEach((item, index) => {
// item.id = index
// })
state.prizeList.splice(4, 0, startBtn)
console.log(state.prizeList)
})第二步:實(shí)現(xiàn)動(dòng)效
思路 跑馬燈效果的實(shí)現(xiàn)主要是轉(zhuǎn)圈高亮,這里我們可以想到當(dāng)前禮物坐標(biāo)和跑馬執(zhí)行步數(shù)除以8的余數(shù)一致時(shí)就高亮,但是跟禮物列表渲染的順序又不一樣,所以我們可以定義一個(gè)跑馬執(zhí)行的禮物坐標(biāo)順序數(shù)組 prizeSort = [0, 1, 2, 5, 8, 7, 6, 3],這樣就是轉(zhuǎn)圈執(zhí)行啦。 然后動(dòng)畫(huà)一步一步的執(zhí)行的話我們可以使用一個(gè)定時(shí)器,然后隔一點(diǎn)時(shí)間讓當(dāng)前高亮的下標(biāo)索引currentIndex變化 轉(zhuǎn)動(dòng)的圈數(shù)我們可以自定義幾圈,這里我們用總執(zhí)行步數(shù)計(jì)算,必須是8的倍數(shù),比如每次要轉(zhuǎn)4圈,那基本的總執(zhí)行步數(shù)就是32步,再根據(jù)后臺(tái)中獎(jiǎng)的禮物坐標(biāo)計(jì)算出還要走多少步加上32即跑馬執(zhí)行的總部數(shù) 轉(zhuǎn)動(dòng)速度的話一般是先快后忙,我們利用定時(shí)器setTimeout()方法的話第二個(gè)參數(shù)的等待時(shí)間就要越來(lái)越長(zhǎng),可以判斷如果執(zhí)行總步數(shù)超過(guò)一般或三分之二就開(kāi)始增加定時(shí)器等待時(shí)間,讓下一步執(zhí)行越來(lái)越慢。
第三步:后臺(tái)抽獎(jiǎng)結(jié)果
思路: 后臺(tái)返回抽獎(jiǎng)結(jié)果的話,這里可能出現(xiàn)上面注意點(diǎn)問(wèn)題4和5,接口報(bào)錯(cuò)和長(zhǎng)時(shí)間等待,我們處理方式可以為,點(diǎn)擊先請(qǐng)求返回結(jié)果再開(kāi)始動(dòng)效,對(duì)于接口報(bào)錯(cuò)我們可以直接提示出來(lái),如何長(zhǎng)時(shí)間等待我們可以增加一個(gè)loading提示,這樣就基本可以解決這兩個(gè)問(wèn)題了。對(duì)于后臺(tái)返回結(jié)果和動(dòng)效執(zhí)行完結(jié)果如何匹配的問(wèn)題,上面說(shuō)過(guò),我們有個(gè)基本的執(zhí)行步數(shù),然后根據(jù)請(qǐng)求結(jié)果的禮物坐標(biāo)計(jì)算出還要走執(zhí)行多少步,相加結(jié)果就可以匹配上。
代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
[v-cloak] {
display: none;
}
.container {
width: 450px;
height: 450px;
background: #98d3fc;
border: 1px solid #98d3fc;
margin: 100px auto;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
}
.item {
width: 140px;
height: 140px;
border: 2px solid #fff;
position: relative;
}
.item:nth-of-type(5) {
cursor: pointer;
}
.item img {
width: 100%;
height: 100%;
}
.item p {
width: 100%;
height: 20px;
background: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 12px;
text-align: center;
line-height: 20px;
position: absolute;
left: 0;
bottom: 0;
}
.active {
border: 2px solid red;
box-shadow: 2px 2px 30px #fff;
}
</style>
</head>
<body>
<div id="app" v-cloak>
<div class="container">
<div :class="['item', {'active': currentIndex === index}]"
v-for="(item, index) in prizeList"
@click="start(index)">
<img :src="item.pic" alt="">
<p v-if="index !== 4">{{ item.name }}</p>
</div>
</div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp, onMounted, ref, reactive, toRefs, computed } = Vue
createApp({
setup () {
const state = reactive({
prizeList: [
{ name: '手機(jī)', pic: 'https://bkimg.cdn.bcebos.com/pic/3801213fb80e7bec54e7d237ad7eae389b504ec23d9e' },
{ name: '手表', pic: 'https://img1.baidu.com/it/u=2631716577,1296460670&fm=253&fmt=auto&app=120&f=JPEG' },
{ name: '蘋(píng)果', pic: 'https://img2.baidu.com/it/u=2611478896,137965957&fm=253&fmt=auto&app=138&f=JPEG' },
{ name: '棒棒糖', pic: 'https://img2.baidu.com/it/u=576980037,1655121105&fm=253&fmt=auto&app=138&f=PNG' },
{ name: '娃娃', pic: 'https://img2.baidu.com/it/u=4075390137,3967712457&fm=253&fmt=auto&app=138&f=PNG' },
{ name: '木馬', pic: 'https://img1.baidu.com/it/u=2434318933,2727681086&fm=253&fmt=auto&app=120&f=JPEG' },
{ name: '德芙', pic: 'https://img0.baidu.com/it/u=1378564582,2397555841&fm=253&fmt=auto&app=120&f=JPEG' },
{ name: '玫瑰', pic: 'https://img1.baidu.com/it/u=1125656938,422247900&fm=253&fmt=auto&app=120&f=JPEG' }
], // 后臺(tái)配置的獎(jiǎng)品數(shù)據(jù)
currentIndex: 0, // 當(dāng)前位置
isRunning: false, // 是否正在抽獎(jiǎng)
speed: 10, // 抽獎(jiǎng)轉(zhuǎn)動(dòng)速度
timerIns: null, // 定時(shí)器實(shí)例
currentRunCount: 0, // 已跑次數(shù)
totalRunCount: 32, // 總共跑動(dòng)次數(shù) 8的倍數(shù)
prizeId: 0, // 中獎(jiǎng)id
})
const startBtn = { name: '開(kāi)始按鈕', pic: 'https://img2.baidu.com/it/u=1497996119,382735686&fm=253' }
// 獎(jiǎng)品高亮順序
const prizeSort = [0, 1, 2, 5, 8, 7, 6, 3]
// 要執(zhí)行總步數(shù)
const totalRunStep = computed(() => {
return state.totalRunCount + prizeSort.indexOf(state.prizeId)
})
onMounted(() => {
// state.prizeList.forEach((item, index) => {
// item.id = index
// })
state.prizeList.splice(4, 0, startBtn)
console.log(state.prizeList)
})
// 獲取隨機(jī)數(shù)
const getRandomNum = () => {
// const num = Math.floor(Math.random() * 9)
// if (num === 4) {
// console.log(">>>>>不能為4")
// return getRandomNum()
// } else {
// return num
// }
// 這里一次必然可以取到 時(shí)間為1次
return prizeSort[Math.floor(Math.random() * prizeSort.length)]
}
const start = (i) => {
if (i === 4 && !state.isRunning) {
// 重置數(shù)據(jù)
state.currentRunCount = 0
state.speed = 100
state.isRunning = true
console.log('開(kāi)始抽獎(jiǎng),后臺(tái)請(qǐng)求中獎(jiǎng)獎(jiǎng)品')
// 請(qǐng)求返回的獎(jiǎng)品編號(hào) 這里使用隨機(jī)數(shù) 但不能為4
// const prizeId = getRandomNum()
// console.log('中獎(jiǎng)ID>>>', prizeId, state.prizeList[prizeId])
// state.prizeId = prizeId
// 模擬接口延時(shí)返回 如果接口突然報(bào)錯(cuò)如何處理?直接調(diào)用stopRun()方法停止轉(zhuǎn)動(dòng)
setTimeout(() => {
const prizeId = getRandomNum()
console.log('中獎(jiǎng)ID>>>', prizeId, state.prizeList[prizeId])
state.prizeId = prizeId
}, 2000)
startRun()
}
}
const startRun = () => {
stopRun()
console.log(state.currentRunCount, totalRunStep.value)
// 要執(zhí)行總步數(shù)
// 已走步數(shù)超過(guò)
if (state.currentRunCount > totalRunStep.value) {
state.isRunning = false
return
}
state.currentIndex = prizeSort[state.currentRunCount % 8]
// 如果當(dāng)前步數(shù)超過(guò)了2/3則速度慢下來(lái)
if (state.currentRunCount > Math.floor(state.totalRunCount * 2 / 3)) {
state.speed = state.speed + Math.floor(state.currentRunCount / 3)
console.log('速度>>>>', state.speed)
}
state.timerIns = setTimeout(() => {
state.currentRunCount++
startRun()
}, state.speed)
}
const stopRun = () => {
state.timerIns && clearTimeout(state.timerIns)
}
return {
...toRefs(state),
start
}
}
}).mount('#app')
</script>
</body>
</html>以上就是Vue3實(shí)現(xiàn)九宮格抽獎(jiǎng)效果的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue3九宮格抽獎(jiǎng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue子組件props從父組件接收數(shù)據(jù)并存入data
這篇文章主要介紹了Vue子組件props從父組件接收數(shù)據(jù)并存入data的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
通過(guò)vue方式實(shí)現(xiàn)二維碼掃碼功能
這篇文章給大家介紹了通過(guò)vue的方式,實(shí)現(xiàn)掃碼功能,實(shí)現(xiàn)步驟分為兩步,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2021-11-11
vue路由導(dǎo)航守衛(wèi)和請(qǐng)求攔截以及基于node的token認(rèn)證的方法
這篇文章主要介紹了vue路由導(dǎo)航守衛(wèi)和請(qǐng)求攔截以及基于node的token認(rèn)證的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04
vue2 中使用 render 函數(shù)編寫(xiě)組件的方式
vue提供了聲明式編寫(xiě)UI的方式,即vue提供了對(duì)DOM進(jìn)行描述的方式,有兩種描述DOM的方式即模板和render 函數(shù),本文通過(guò)示例代碼介紹vue2 中使用 render 函數(shù)編寫(xiě)組件的方式,感興趣的朋友跟隨小編一起看看吧2024-06-06
element-ui 表格實(shí)現(xiàn)單元格可編輯的示例
下面小編就為大家分享一篇element-ui 表格實(shí)現(xiàn)單元格可編輯的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02

