Vue.js 做一个大屏幕抽奖

在 Github 上面搜索 “lottery”,发现类似的项目挺多的,有 Electron 实现的,可以打包成 exe,也有原生 js 实现的,浏览器打开。

这次是用 Vue 做了一个编号抽奖,一个转盘抽奖,放在一起,练手。

预览:http://lottery.imaegoo.com/

编号抽奖

编号抽奖实现起来非常简单,存一个号码池,存一个抽奖历史,从号码池中过滤掉已中奖的号码,从剩下的号码随机出一个就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<template>
<div class="lottery">
<div class="current">{{ current }}</div>
<button class="button is-large is-warning" v-if="!timer" @click="start">开始</button>
<button class="button is-large is-danger" v-if="timer" @click="stop"></button>
<table class="table" v-if="history.length > 0">
<thead>
<tr>
<th>轮次</th>
<th>中奖号码</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in history" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ item }}</td>
</tr>
</tbody>
</table>
</div>
</template>

<script>
export default {
data () {
return {
numbers: [],
current: '',
timer: null,
history: []
}
},
computed: {
neverWin () {
return this.numbers.filter((item) => !this.history.some((historyItem) => historyItem === item))
}
},
methods: {
random (minNum, maxNum) {
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum)
},
start () {
if (this.neverWin.length < 1) {
alert('所有人都已中过奖! 请到设置清空中奖纪录!')
return
}
this.timer = setInterval(this.refresh, 100)
},
stop () {
clearInterval(this.timer)
this.timer = null
this.saveHistory()
},
refresh () {
let next
do {
next = this.neverWin[this.random(0, this.neverWin.length - 1)]
} while (this.current === next) // 避免同数值被连续刷到
this.current = next
},
saveHistory () {
this.history.push(this.current)
localStorage.setItem('history', JSON.stringify(this.history))
},
readNumbers () {
const numStr = localStorage.getItem("numbers")
if (numStr) this.numbers = numStr.split('\n').filter((item) => !!item)
if (this.numbers.length === 0) {
alert('抽奖池为空! 请到右上角设置中添加!')
this.numbers = ['11', '22', '33', '44', '55', '66', '77', '88', '99']
}
},
readHistory () {
const history = localStorage.getItem("history")
if (history) this.history = JSON.parse(history)
}
},
mounted () {
this.readNumbers()
this.readHistory()
this.current = this.numbers[0].split('').map(() => '*').join('')
}
}
</script>

转盘抽奖

转盘抽奖稍微复杂一点:

  1. 随机抽一个目标奖项
  2. 计算得出到这个奖项的转动角度(这里的角度我也是在区间中随机取,看上去更真实)
  3. 加上(360° × 转多少圈)
  4. 绑定到转盘图片的 transform
  5. 设置旋转动画 transition: transform 5s;

做完之后发现也不是很难。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<template>
<div class="rotate">
<div class="arrow">
<img class="wheel"
src="../assets/wheel.png"
:style="{transform: `rotate(${currentAngle}deg)`}" />
<div class="pointer" @click="startArrow"></div>
</div>
<div class="text" v-if="currentAward">{{ currentAward }}</div>
</div>
</template>

<script>
const awards = [
'侯校长发红包', // 30%
'工作日不扣工资,休息一天', // 10%
'与黄书记共进晚餐', // 10%
'工作日不扣工资,休息半天', // 10%
'教研组最美(最帅)的人请喝奶茶', // 15%
'跟校长们合张影' // 25%
]
export default {
props: {
msg: String
},
data() {
return {
currentAngle: 0,
times: 0,
currentAward: '等待抽奖'
}
},
methods: {
startArrow() {
if (this.currentAward === '正在抽奖') return
let award = this.getAward()
this.getAngle(award)
this.setCurrentAward(award)
},
getAward() {
let randomNum = Math.random()
if (randomNum < 0.3) {
return 0
} else if (randomNum < 0.4) {
return 1
} else if (randomNum < 0.5) {
return 2
} else if (randomNum < 0.6) {
return 3
} else if (randomNum < 0.75) {
return 4
} else if (randomNum <= 1) {
return 5
}
},
getAngle(award) {
this.times++
let max = (award + 1) * 60
let min = award * 60
let angle = parseInt(Math.random() * (max - min + 1) + min, 10)
this.currentAngle = 360 * (this.times * 7) + angle
},
setCurrentAward(award) {
this.currentAward = '正在抽奖'
setTimeout(() => {
this.currentAward = `恭喜中奖:${awards[award]}`
}, 5000)
}
}
}
</script>

<style scoped>
.arrow {
position: relative;
}
.wheel {
transition: transform 5s;
}
.pointer {
background-repeat: no-repeat;
background-position: center;
background-image: url('../assets/arrow.png');
content: '';
top: 0;
left: 0;
right: 0;
bottom: 0;
position: absolute;
}
.text {
margin-top: 20px;
font-size: 26px;
font-weight: bold;
}
</style>

指针 arrow.png 和转盘 wheel.png 两张图片可以在 https://github.com/imaegoo/lottery/tree/zhengzhou/src/assets 找到。

路由

反正只有4个页面,直接自己写一个 tab 切换,没有引入 vue-router。

UI框架

功能太简单也用不到,只用了一个 Bulma。
啊~ 轻量又好用的 Bulma~

总结

源码放在了 https://github.com/imaegoo/lottery 上面。

设计、切图、编码、测试总共花了6小时,嗯,挺简单的。

评论