Vue3实现九宫格抽奖(思路+完整代码)

目录

前言

一、实现思路

1.组件结构

2.数据结构

①奖品列表

②抽奖按钮

③v-for的抽奖列表

3.组件交互

①抽奖顺序

②奖品高亮

③中奖

二、完整代码


前言

虽然有一些抽奖插件比如lucky-canvas来帮助我们快速发开抽奖小活动,但一些高定制的项目,只能自己手写抽奖(组件构成复杂,插件的css满足不了),今天记录一个九宫格抽奖demo,走一遍抽奖活动设计思路。

一、实现思路

假设要实现如下的效果图:(网上截的图,css意思意思就行,具体项目按照设计稿来)

1.组件结构

九宫格抽奖的组件结构很简单。首先肯定要有一个父盒子容器,来存放奖品和抽奖按钮。所以优先使用flex布局。

接着就是各种不同的奖品item,和抽奖按钮安置其中。推荐用v-for循环出来(也可以直接手写出来9个奖品盒子)。具体prize-item的盒子里面有什么,取决于需求。以下是参考图简单实现:

<template>
  <div class="container">
    <div v-for="(item, index) in DrawList" :key="index" class="prize-item">
      <img :src="item.url" alt="" />
      <p class="desc">{{ item.desc }}</p>
    </div>
  </div>
</template>

2.数据结构

①奖品列表

项目里,奖项有可能是前端固定,也有可能是后台返回的数据,所以放在一个数组里进行管理。

②抽奖按钮

抽奖按钮有可能是文字、图片。还有可能在抽奖过程中改变,所以用一个对象管理。

③v-for的抽奖列表

奖品列表和抽奖按钮加起来,就是完整的渲染列表,由于跟随前两者变化,推荐使用计算属性。

<script setup lang="ts">
  import none from '../assets/none.png'
  import topPrize from '../assets/top-prize.png'
  import secondPrize from '../assets/second-prize.png'
  import thirdPrize from '../assets/third-prize.png'
  import fourthPrize from '../assets/fourth-prize.png'
  import redEnvelope from '../assets/red-envelope.png'

  interface Prize {
    url: string
    desc: string
  }

  const pirzeList: Prize[] = [
    { url: redEnvelope, desc: '现金红包' },
    { url: topPrize, desc: '一等奖' },
    { url: thirdPrize, desc: '三等奖' },
    { url: none, desc: '谢谢参与' },
    { url: secondPrize, desc: '二等奖' },
    { url: redEnvelope, desc: '现金红包' },
    { url: none, desc: '谢谢参与' },
    { url: fourthPrize, desc: '四等奖' }
  ]
  const btnStart = { url: '', desc: '开始抽奖' }
  const DrawList = computed(() => {
    return [...pirzeList.slice(0, 4), btnStart, ...pirzeList.slice(4)]
  })
</script>

至此,ui就呈现出来了。

3.组件交互

①抽奖顺序

九宫格抽奖一般是顺时针旋转,所以需要一个抽奖顺序,让它按照顺时针进行。(也有随机位置的,那更简单,一个Math.random控制index就可以了。)

 而不是按照抽奖列表数组的index进行,那会变成这样:

于是这里专门根据顺时针对应的drawList的index来组成了一个数组drawOrder:

    const drawOrder = [0, 1, 2, 5, 8, 7, 6, 3] // 抽奖顺序
    const currentIndex = ref<number | null>(null) // 当前选中的奖品
②奖品高亮

专门用一个active类来表示选中高亮,只要抽奖的当前的位置等于列表里面的奖品位置,该项高亮。

    .active {
      background-color: rgb(214, 83, 83);
    }
③中奖

首先需要判断只有抽奖列表里面的抽奖按钮才能触发click事件。也就是index===4。

其次开始抽奖后不能再抽奖了,所以需要一个变量表示是否在抽奖。(防抖效果)

最后抽奖次数倒数为0的时候停下中奖。所以需要定义总抽奖次数(多久后停下来)

  let count = 0 // 抽奖次数
  let isDrawing = false // 是否正在抽奖
  const circle = 32 // 一圈8个奖品,至少转4圈

这里具体是哪个奖品中奖是从后台获取。为了方便,以下代码直接写成第五个。

const draw = (index: number) => {
    if (index === 4) {
      // 开始抽奖
      if (isDrawing) {
        return
      }
      isDrawing = true

      const position = 5 // 假设后台返回的中奖位置是5

      const timer = setInterval(() => {
        currentIndex.value = drawOrder[count % drawOrder.length]
        count++
        if (count > circle && currentIndex.value === drawOrder[position - 1]) {
          // 抽奖结束
          clearInterval(timer)
          // 停顿一会显示中奖
          setTimeout(() => {
            alert('恭喜你中奖了')
            isDrawing = false
            count = 0
            currentIndex.value = null
          }, 500)
        }
      }, 50)
    }
  }

中间的逻辑自行替换。需要替换的有:

  • 圈数 --> 时间(可能)
  • position从后台获取 
  • 抽奖完成后的其他逻辑

二、完整代码

<template>
  <div class="container">
    <div
      v-for="(item, index) in DrawList"
      :key="index"
      class="prize-item"
      :class="currentIndex === index ? 'active' : ''"
      @click="() => draw(index)"
    >
      <img :src="item.url" alt="" />
      <p class="desc">{{ item.desc }}</p>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { ref, computed } from 'vue'
  import none from '../assets/none.png'
  import topPrize from '../assets/top-prize.png'
  import secondPrize from '../assets/second-prize.png'
  import thirdPrize from '../assets/third-prize.png'
  import fourthPrize from '../assets/fourth-prize.png'
  import redEnvelope from '../assets/red-envelope.png'

  interface Prize {
    url: string
    desc: string
  }

  const pirzeList: Prize[] = [
    { url: redEnvelope, desc: '现金红包' },
    { url: topPrize, desc: '一等奖' },
    { url: thirdPrize, desc: '三等奖' },
    { url: none, desc: '谢谢参与' },
    { url: secondPrize, desc: '二等奖' },
    { url: redEnvelope, desc: '现金红包' },
    { url: none, desc: '谢谢参与' },
    { url: fourthPrize, desc: '四等奖' }
  ]
  const btnStart = { url: '', desc: '开始抽奖' }
  const DrawList = computed(() => {
    return [...pirzeList.slice(0, 4), btnStart, ...pirzeList.slice(4)]
  })
  const drawOrder = [0, 1, 2, 5, 8, 7, 6, 3] // 抽奖顺序
  let count = 0 // 抽奖次数
  let isDrawing = false // 是否正在抽奖
  const currentIndex = ref<number | null>(null) // 当前选中的奖品
  const circle = 32 // 一圈8个奖品,至少转4圈

  const draw = (index: number) => {
    if (index === 4) {
      // 开始抽奖
      if (isDrawing) {
        return
      }
      isDrawing = true

      const position = 5 // 假设后台返回的中奖位置是5

      const timer = setInterval(() => {
        currentIndex.value = drawOrder[count % drawOrder.length]
        count++
        if (count > circle && currentIndex.value === drawOrder[position - 1]) {
          // 抽奖结束
          clearInterval(timer)
          // 停顿一会显示中奖
          setTimeout(() => {
            alert('恭喜你中奖了')
            isDrawing = false
            count = 0
            currentIndex.value = null
          }, 500)
        }
      }, 50)
    }
  }
</script>

<style lang="scss" scoped>
  .container {
    width: 528px;
    height: 528px;
    background: url('../assets/bg.png') no-repeat;
    padding: 55px 55px;

    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-content: space-between;

    .prize-item {
      width: 130px;
      height: 130px;
      text-align: center;

      img {
        margin-top: 10px;
      }
    }

    .active {
      background-color: rgb(214, 83, 83);
    }
  }
</style>

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值