实现转盘抽奖功能

1、实现转盘数据动态配置(可通过接口获取)

2、背景色通过分隔配置

3、转动速度慢慢减速,最后停留在每一项的中间,下一次开始从本次开始

4、当动画停止后在对应事件中自定义生成中奖提示。

5、本次中奖概率随机生成,也可自定义配置

实现代码

html

js 复制代码
<template>
  <div class="graph-page">
    <div class="plate-wrapper" :style="`${bgColor};`">
      <div class="item-plate" :style="plateCss(index)" v-for="(item, index) in plateList" :key="index" >
        <img :src="item.pic" alt="">
        <p>{{item.name}}</p>
      </div>
    </div>
    <div @click="handleClick" class="btn"></div>
  </div>
</template>

css

js 复制代码
<style lang="less" scoped>
.graph-page {
  width: 540px;
  height: 540px;
  margin: 100px auto;
  position: relative;
}
.plate-wrapper {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 10px solid #98d3fc;
  overflow: hidden;
}
.item-plate {
  position: absolute;
  left: 0;
  right: 0;
  top: -10px;
  margin: auto;
}
.item-plate img {
  width: 30%;
  height: 20%;
  margin: 40px auto 10px;
  display: block;
}
.item-plate p {
  color: #fff;
  font-size: 12px;
  text-align: center;
  line-height: 20px;
}
.btn {
  width: 160px;
  height: 160px;
  background: url('https://www.jq22.com/demo/jquerylocal201912122316/img/btn_lottery.png') no-repeat center / 100% 100%;
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  margin: auto;
  cursor: pointer;
}
.btn::before {
  content: "";
  width: 41px;
  height: 39px;
  background: url('https://www.jq22.com/demo/jquerylocal201912122316/img/icon_point.png') no-repeat center / 100% 100%;
  position: absolute;
  left: 0;
  right: 0;
  top: -33px;
  margin: auto;
}
</style>

js

其中背景色采用间隔配置,扇形背景采用锥形渐变函数conic-gradient可实现。

每个转项的宽度和高度可参照以下图片,所有奖品的div都定位在圆心以上,根据圆心转动,所以旋转点为底部中心,即:transform-origin: 50% 100%;

可采用监听transitionend事件判断动画是否结束,可自定义中奖提示。

js 复制代码
<script>
export default {
  data() {
    return {
      plateList: [],
      isRunning: false, //判断是否正在转动
      rotateAngle: 0, //转盘每项角度
      baseRunAngle: 360 * 5, //总共转动角度,至少5圈
      totalRunAngle: 0, //要旋转的总角度
      activeIndex: 0, //中奖index
      wrapDom: null //转盘dom
    }
  },
  computed: {
    bgColor(){ //转盘的每项背景
      let len = this.plateList.length
      let color = ['#5352b3', '#363589']
      let colorVal = ''
      this.plateList && this.plateList.forEach((item, index)=>{
        colorVal += `${color[index % 2]} ${(360/len)*index}deg ${(360/len)*(index+1)}deg,`
      })
      return `background: conic-gradient(${colorVal.slice(0, -1)})`
    },
    plateCss(){ //转盘的每项样式
      if(this.plateList && this.plateList.length){
        return (i) => {
          return `
            width: ${Math.floor(2 * 270 * Math.sin(this.rotateAngle / 2 * Math.PI / 180))}px;
            height: 270px;
            transform: rotate(${this.rotateAngle * i + this.rotateAngle / 2}deg);
            transform-origin: 50% 100%;
          `
        }
      }
      return ()=>{''}
    },
  },
  created(){
    this.plateList= [
      { name: '手机', pic: 'https://bkimg.cdn.bcebos.com/pic/3801213fb80e7bec54e7d237ad7eae389b504ec23d9e' },
      { name: '手表', pic: 'https://img1.baidu.com/it/u=2631716577,1296460670&fm=253&fmt=auto&app=120&f=JPEG' },
      { name: '苹果', pic: 'https://img2.baidu.com/it/u=2611478896,137965957&fm=253&fmt=auto&app=138&f=JPEG' },
      { name: '棒棒糖', pic: 'https://img2.baidu.com/it/u=576980037,1655121105&fm=253&fmt=auto&app=138&f=PNG' },
      { name: '娃娃', pic: 'https://img2.baidu.com/it/u=4075390137,3967712457&fm=253&fmt=auto&app=138&f=PNG' },
      { name: '木马', pic: 'https://img1.baidu.com/it/u=2434318933,2727681086&fm=253&fmt=auto&app=120&f=JPEG' },
      { name: '德芙', pic: 'https://img0.baidu.com/it/u=1378564582,2397555841&fm=253&fmt=auto&app=120&f=JPEG' },
      { name: '玫瑰', pic: 'https://img1.baidu.com/it/u=1125656938,422247900&fm=253&fmt=auto&app=120&f=JPEG' }
    ]
    this.rotateAngle = 360 / this.plateList.length
    this.totalRunAngle = this.baseRunAngle + 360 - this.activeIndex * this.rotateAngle - this.rotateAngle / 2 
  },
  mounted(){
    this.$nextTick(()=>{
      this.wrapDom = document.getElementsByClassName('plate-wrapper')[0]
    })
  },
  beforeDestroy(){
    this.wrapDom.removeEventListener('transitionend', this.stopRun)
  },
  methods:{
    handleClick(){
      if(this.isRunning) return 
      this.isRunning = true
      const ind = Math.floor(Math.random() * this.plateList.length)//通过随机数返回奖品编号
      this.activeIndex = ind
      this.startRun()
    },
    startRun(){
      // 设置动画
      this.wrapDom.setAttribute('style', `
        ${this.bgColor};
        transform: rotate(${this.totalRunAngle}deg);
        transition: all 4s ease;
      `)
      this.wrapDom.addEventListener('transitionend', this.stopRun) // 监听transition动画停止事件
    },
    stopRun(){
      this.isRunning = false
      this.wrapDom.setAttribute('style', `
        ${this.bgColor};
        transform: rotate(${this.totalRunAngle - this.baseRunAngle}deg);
      `)
    }
  }
}
</script>

参考来源:juejin.cn/post/718031...

相关推荐
ZXT2 分钟前
js基础重点复习
javascript
言兴9 分钟前
秋招面试---性能优化(良子大胃袋)
前端·javascript·面试
WebInfra1 小时前
Rspack 1.5 发布:十大新特性速览
前端·javascript·github
雾恋2 小时前
我用 trae 写了一个菜谱小程序(灶搭子)
前端·javascript·uni-app
烛阴2 小时前
TypeScript 中的 `&` 运算符:从入门、踩坑到最佳实践
前端·javascript·typescript
Java 码农3 小时前
nodejs koa留言板案例开发
前端·javascript·npm·node.js
ZhuAiQuan4 小时前
[electron]开发环境驱动识别失败
前端·javascript·electron
nyf_unknown4 小时前
(vue)将dify和ragflow页面嵌入到vue3项目
前端·javascript·vue.js
胡gh4 小时前
数组开会:splice说它要动刀,map说它只想看看。
javascript·后端·面试
胡gh4 小时前
浏览器:我要用缓存!服务器:你缓存过期了!怎么把数据挽留住,这是个问题。
前端·面试·node.js