Vue实现九宫格抽奖

159 阅读1分钟

事件起因

参照掘金每日的抽奖和别的九宫格活动

实现思路

  • 采用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>

最终实现效果

image.png