前言
事情是這樣的,作為一個意志力極低的人,最近一直在找尋提高意志力的方法,
然后決定試一試所謂的“建立獎勵機制”,也就是說,完成一項意志力挑戰后給自己一些獎勵(具體操作方法不在這里進行贅述),
那么,一款絲滑的“抽獎頁面”也就理所當然加入了我的(瞎)待(折)辦(騰)事項中,
整個程序還是比較簡單的,涉及到的知識也相對基礎,作為這個博客的第一篇技術文章,再加上作為一個技術小白,寫的相對簡單,希望能帶個大家些許幫助,如果有建議批評也歡迎大家踴躍反饋,
涉及到的技術堆疊/框架
- 前端開發基礎知識(Html/Css/JavaScript)
- Javascript ES6語法
- Vue.js相關技術知識
- JavaScript異步知識
基礎代碼
先上一段整體的基礎代碼
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lottery</title>
<style>
#app {
margin: auto;
width: 200px;
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<p style="height:22px">{{item}}</p>
<button @click="start">開始!</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</body>
<script>
const app = new Vue({
el: "#app",
data: {
list: ['1積分', '5積分', '10積分', '20積分', '謝謝惠顧', '再來一次'],
item: "開始抽獎!"
},
methods: {
start() {
for (let index = 0; index < 50; index++) {
setTimeout(() => {
this.item = this.list[Math.round(Math.random() * (this.list.length - 1))]
}, 100 + index * 100)
}
}
}
})
</script>
</html>
可以看到整體頁面元素是再簡單不過的,頁面上除了 最外層的div元素 之外,就只剩一個用于啟動抽獎的 按鈕 和一個用于顯示當前Item的 段落元素,通過外鏈的方式參考了 Vue框架,
data中,list陣列 作為獎池,用于放置可以被抽取到的item,item變數 則用來指明當前抽取到的item并與用于顯示當前item的 段落元素 進行系結 ,在 start方法 執行之前,item變數 默認值為“開始抽獎!”,
methods中只有一個 start方法 用于啟動抽獎,通過 for回圈 ,進行50次的抽取,setTimeOut 將抽取時間控制在100毫秒,也就是說每100毫秒進行一次抽取,從 list陣列 中隨機抽取值替換當前的 item變數,
效果如下圖??

需求
我們現有的基礎功能毫無疑問是過于簡陋的,僵硬的抽取程序,無嚴謹可言的抽取規則等等,讓我們的抽獎系統現在看起來就像是
并夕夕的 “贏取百萬現金”輪盤一樣透露和不可言說的廉價感,那么“如何改善”就顯得尤為關鍵了,
- 優化抽取程序,使抽取程序流暢不那么僵硬
- 優化抽取規則,使抽取規則更為嚴謹
- 優化完善部分細節
完善程序
優化抽取程序
由于item抽取的間隔時間都為固定的100毫秒,看起來很呆板不絲滑,所以對抽取的間隔時間做出優化,讓它看起來絲滑流暢一些,
- start 方法的更新:
async start() {
for (let index = 0; index < 50; index++) {
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.list[Math.round(Math.random() * (this.list.length - 1))])
}, Math.round(this.getTime(index)))
})
}
},
將異步函式 setTimeOut 進行 await 異步處理,讓間隔時間可以疊加方便我們進行間隔時間的控制,由于使用了es6語法中的 await 關鍵字,所以此函式加上了 async 前綴,間隔時間使用新添加的函式 getTime 傳參獲取,
- 新增 getTime 方法
getTime(index) {
// 初始/正常間隔時間
let time = 80
// 啟動間隔時間處理
if (index <= 10) {
time = time + (11 - index) * 20
}
// 結束間隔時間的處理
if (index >= 39) {
time = time + (index - 39) * 20
if (index === 49) time = 1000
}
return time
}
通過引數 index 獲取當時的回圈次數,并計算出本次對應的 間隔時間 進行回傳,(簡單寫的一個方法,且只針對于本專案,以后有空再進行封裝完善,間隔時間的規律演算法只經過了作者簡單的計算,讓它看起來比較符合作者對“順滑”的定義,大家要是不喜歡也可以自己去嘗試更改,畢竟現在是實驗專案,能用就行)
優化抽取規則
由于抽取的規則并沒有添加任何限制,導致專案抽取item的時候很可能抽取到的item與上一個相同,那就導致item在顯示切換的時候出現問題,看起來好像并沒有進行抽取,
- start 方法的更新:
async start() {
for (let index = 0; index < 50; index++) {
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.getItem())
}, Math.round(this.getTime(index)))
})
}
},
不再是簡單的回傳抽取結果,而是回傳 getItem 方法計算出的item,對抽取的item結果添加限制,
- 新增 getItem 方法
getItem() {
let newItem;
do {
newItem = this.list[Math.round(Math.random() * (this.list.length - 1))]
} while (this.item === newItem);
return newItem
}
如果抽取到與上一個item相同的結果,那么就進行重復抽取,直到抽取到不同的結果為止,
優化細節
data: {
startButtonDisabled: false,
finished: false,
list: ['1積分', '5積分', '10積分', '20積分', '謝謝惠顧', '再來一次'],
item: "開始抽獎!"
},
在 data 中加入 startButtonDisabled 和 finished 用來分別記錄按鈕狀態以及開獎的狀態,
handleStart(index) {
if (index === 0) {
this.finished = false
this.startButtonDisabled = true
}
},
handleEnd(index) {
if (index === 49) {
this.finished = true
this.startButtonDisabled = false
}
}
添加函式 handleStart 和 handleEnd對 start 方法中回圈體的首尾添加事件進行細節上的優化,在傳入引數 index 為0(回圈開始)時,利用方法 handleStart 將 開獎狀態 改變為未開獎,按鈕禁用,且在傳入引數為49(回圈結束)時,利用方法 handleEnd 再次改變其狀態,
async start() {
for (let index = 0; index < 50; index++) {
this.handleStart(index)
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.getItem())
}, Math.round(this.getTime(index)))
})
this.handleEnd(index)
}
},
在 start 方法的回圈體首尾呼叫 handleStart 和 handleEnd 方法,
<div id="app">
<p : style="height:22px">{{item}}</p>
<button :disabled="startButtonDisabled" @click="start">開始!</button>
</div>
將頁面元素進行修改,使對應元素會隨著 data 中資料狀態的改變而產生變化,
p.active {
font-weight: bold;
color: red
}
最后為開獎狀態為true的 active 類添加css樣式,優化完成!
最終代碼
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lottery</title>
<style>
#app {
margin: auto;
width: 200px;
text-align: center;
}
p.active {
font-weight: bold;
color: red
}
</style>
</head>
<body>
<div id="app">
<p : style="height:22px">{{item}}</p>
<button :disabled="startButtonDisabled" @click="start">開始!</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</body>
<script>
const app = new Vue({
el: "#app",
data: {
startButtonDisabled: false,
finished: false,
list: ['1積分', '5積分', '10積分', '20積分', '謝謝惠顧', '再來一次'],
item: "開始抽獎!"
},
methods: {
async start() {
for (let index = 0; index < 50; index++) {
this.handleStart(index)
this.item = await new Promise(resolve => {
setTimeout(() => {
resolve(this.getItem())
}, Math.round(this.getTime(index)))
})
this.handleEnd(index)
}
},
getTime(index) {
// 初始/正常間隔時間
let time = 80
// 啟動間隔時間處理
if (index <= 10) {
time = time + (11 - index) * 20
}
// 結束間隔時間的處理
if (index >= 39) {
time = time + (index - 39) * 20
if (index === 49) time = 1000
}
return time
},
getItem() {
let newItem;
do {
newItem = this.list[Math.round(Math.random() * (this.list.length - 1))]
} while (this.item === newItem);
return newItem
},
handleStart(index) {
if (index === 0) {
this.finished = false
this.startButtonDisabled = true
}
},
handleEnd(index) {
if (index === 49) {
this.finished = true
this.startButtonDisabled = false
}
}
}
})
</script>
</html>
效果如下??

結語
一個簡單到不簡單的前端例子而已,沒有進行完善的封裝,就這樣發布了,希望嫩個夠對大家有所幫助,以后我會盡量經常發布一些有營養的技術案例等等同時也會記錄我在學習作業程序中遇到的有趣的知(坑)識,
感謝閱讀,祝你生活愉快,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/296369.html
標籤:JavaScript
上一篇:作業記錄:8個有用的JS技巧
