事件起因
参照掘金每日的抽奖和别的九宫格活动
实现思路
- 采用flex布局
- 模拟请求,随机生成选中的值
- 使用settimeout宏任务,通过改变延迟的时间,递归调用move方法,来实现动画转动的快慢
代码逻辑如下
- template部分
<template>
<div class="cur-page">
<div class="sudoku-view">
<div class="award-item"
v-for="(item, index) in awards"
:key="item.id"
:class="{'active-item': index === current, 'begin-item': item.id === 0 }"
@click="startBtnClicked(item)">
{{ item.name }}
</div>
</div>
<div v-if="selAward.id && !timeOut" style="margin-top: 20px; padding-left: 20px;">恭喜您,中奖结果为:{{selAward.name}}</div>
</div>
</template>
- js部分
<script setup>
import { ref } from 'vue'
const awards = [
{ id: 1, name: '1' },
{ id: 2, name: '2' },
{ id: 3, name: '3' },
{ id: 4, name: '4' },
{ id: 0, name: '开始' },
{ id: 5, name: '5' },
{ id: 6, name: '6' },
{ id: 7, name: '7' },
{ id: 8, name: '8' }
]
const SPEED_VALUE = 200
const DIFF_VALUE = 15
const current = ref(0) // 当前选中
const speed = ref(SPEED_VALUE) // 速度
const diff = ref(DIFF_VALUE) // 增速
const selAward = ref({}) // 选中的值
const time = ref(0) // 记录开始抽奖的时间
let timeOut = ref(null)
const startBtnClicked = (item) => {
if (item.id === 0 && !timeOut.value) {
requestInfo()
}
}
const requestInfo = () => { // 模拟请求中奖
setTimeout(() => {
let index = Math.floor(Math.random() * 9)
if (index === 4) {
index += 1
}
selAward.value = awards[index]
}, 2000)
move()
}
const move = () => { // 开始移动
timeOut.value = setTimeout(() => {
current.value++
if (current.value === 4) {
current.value++
} else if (current.value > 8) {
current.value = 0
}
// 若选中物品,则开始减速转动
if (selAward.value.id && ( Date.now() - time.value) / 1000 > 2) {
speed.value += diff.value // 减速
// 若转动时间超过4秒,并且选中的奖品id等于抽奖格的奖品id,则停下来
if (( Date.now() - time.value ) / 1000 > 4 && selAward.value.id === awards[current.value].id) {
clearTimeout(timeOut.value )
resetData()
return
}
} else { // 没选中奖品,则加快速度
speed.value -= diff.value
}
move()
}, speed.value)
}
const resetData = () => { // 重置相关参数
timeOut.value = null
time.value = 0
speed.value = SPEED_VALUE
}
</script>
- css部分
<style lang="less" scoped>
.sudoku-view {
margin-top: 20px;
width: 350px;
height: 350px;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
.award-item {
width: 100px;
height: 100px;
border-radius: 8px;
color: #333;
background-color: lightskyblue;
line-height: 100px;
text-align: center;
}
.active-item {
background-color: lightcoral;
}
.begin-item {
background-color: lawngreen;
color: #fff;
font-weight: bold;
cursor: pointer;
}
}
</style>