转盘小游戏

作者:陈佳鑫
时间:2022-10-25

使用 VUE CSS 动画编写转盘小游戏

一等奖
二等奖
三等奖
四等奖
五等奖
六等奖
谢谢惠顾
再来一次

通过 CSS transform 设置 div 旋转得到夹角,使用 css 动画animation: rotate 10s 1 ease-in-out; 进行整体 DIV 旋转 10 秒内总共旋转 10 圈旋转方式由慢到快,在由快到慢直至停下, 点击抽奖获取当前中奖位置,计算旋转停下后位置放入--prizeAnglescss 变量

计算每个奖品夹角

通过中奖数组个数计算当前每个奖品夹角大小

calculationAngle(index, isSkewY) {
  // 获取当前奖品数组个数
  const number = this.prizeList.length;
  // 奖品个数是否能被360°圆型整除
  let surplus = 360 % number;
  // 默认圆形角度
  let angle = 360;
  // 如果数组个数不能整除360°圆形将多余部分留出
  if (surplus !== 0) {
    angle = angle - surplus;
  }
  // 计算得出每个奖品夹角度数
  let value = angle / number;
  // 计算奖品夹角开始度数
  let rotate = value * index - value / 2;
  // 计算skewY度数等大三角形div
  let skewY = 90 - value;
  // 渲染最后一个奖品时增加之前留出不能整除角度
  if (index === number - 1) {
    skewY -= surplus;
  }
  // isSkew判断是开始角度还是结束角度
  return isSkewY ? skewY : rotate;
}

计算本次中奖数据

通过随机数生成,计算本次抽奖奖品,获取到当前奖品顺序后,计算奖品在圆盘角度,最后计算圆盘停止角度为奖品角度中间值

uckDraw() {
  // 通过随机数计算本次抽奖奖品
  const prize = Math.abs(
    Math.round(Math.random() * this.prizeList.length - 1)
  );
  // 计算每个奖品夹角,取整
  const includedAngle = parseInt(360 / this.prizeList.length);
  // 计算当前中奖角度 360 - (this.calculationAngle(prize) + includedAngle / 2)
  // 用停止角度加上中奖角度得到转盘停止位置
  this.prizeAngle = `rotate(${3600 + 360 - (this.calculationAngle(prize) + includedAngle / 2)}deg)`;
  // 本次抽中奖品名称
  this.prizeName = this.prizeList[prize].name
  // 开始抽奖
  this.isTake = true;
  // 10秒出现抽奖按钮
  setTimeout(() => {
    this.isTake = false;
  },10000)
}

整体代码###

<template>
  <div class="index" :style="{ '--prizeAngle': prizeAngle }">
    <div ref="container"></div>
    <div
      :style="{
        width: width + 2 + 'px',
        position: 'relative',
        margin: 'auto',
        overflow: 'hidden'
      }"
    >
      <div
        :class="isTake ? 'disc move' : 'disc'"
        :style="{
          width: width + 'px',
          height: `${width}px`,
          'border-radius': `${width}px`,
          transform: prizeAngle
        }"
      >
        <div
          v-for="(item, index) in prizeList"
          :key="item.index"
          class="part constellation-part"
          :style="{
            transform: `rotate(${calculationAngle(
              index
            )}deg) skewY(-${calculationAngle(index, true)}deg)`
          }"
        >
          <div
            class="text"
            :style="{
              width: `${(width / prizeList.length) * 2}px`,
              top: `${width * 0.25}px`,
              transform: `skewY(${calculationAngle(index, true)}deg)`
            }"
          >
            {{ item.name }}
          </div>
        </div>
      </div>
      <div
        class="triangle-up"
        :style="{
          'border-bottom': `${width / 2}px solid red`,
          left: `${width / 2 - 4}px`
        }"
      />
      <div
        class="circular"
        :style="{
          width: `${width / 10}px`,
          height: `${width / 10}px`,
          top: `${width / 2 - width / 20}px`,
          left: `${width / 2 - width / 20}px`,
          'border-radius': `${width / 10}px`
        }"
      />
    </div>
    <button v-if="!isTake" class="but" @click="luckDraw">抽 奖</button>
    <div v-else class="prizeName">中奖:{{ prizeName }}</div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      width: 0,
      isTake: false,
      prizeAngle: 0,
      prizeList: [
        { index: 1, name: '一等奖', color: 'red' },
        { index: 2, name: '二等奖', color: 'red' },
        { index: 3, name: '三等奖', color: 'red' },
        { index: 4, name: '四等奖', color: 'red' },
        { index: 5, name: '五等奖', color: 'red' },
        { index: 6, name: '六等奖', color: 'red' },
        { index: 7, name: '谢谢惠顾', color: 'red' },
        { index: 8, name: '再来一次', color: 'red' }
      ],
      prizeName: null,
      style: {}
    }
  },
  mounted() {
    const width = this.$refs?.container.scrollWidth
    this.width = width
    window.onresize = () => {
      return (() => {
        console.log(this.$refs?.container.scrollWidth)
        this.width = parseInt(this.$refs?.container.scrollWidth)
      })()
    }
  },
  methods: {
    calculationAngle(index, isSkewY) {
      // 获取当前奖品数组个数
      const number = this.prizeList.length
      // 奖品个数是否能被360°圆型整除
      let surplus = 360 % number
      // 默认圆形角度
      let angle = 360
      // 如果数组个数不能整除360°圆形将多余部分留出
      if (surplus !== 0) {
        angle = angle - surplus
      }
      // 计算得出每个奖品夹角度数
      let value = angle / number
      // 计算奖品夹角开始度数
      let rotate = value * index - value / 2
      // 计算skewY度数等大三角形div
      let skewY = 90 - value
      // 渲染最后一个奖品时增加之前留出不能整除角度
      if (index === number - 1) {
        skewY -= surplus
      }
      // isSkew判断是开始角度还是结束角度
      return isSkewY ? skewY : rotate
    },
    // 计算本次中奖
    luckDraw() {
      // 通过随机数计算本次中奖
      const prize = Math.abs(
        Math.round(Math.random() * this.prizeList.length - 1)
      )
      // 计算每个奖品夹角,取整
      const includedAngle = parseInt(360 / this.prizeList.length)
      // 计算当前中奖角度 360 - (this.calculationAngle(prize) + includedAngle / 2)
      // 用停止角度加上中奖角度得到转盘停止位置
      this.prizeAngle = `rotate(${
        3600 + 360 - (this.calculationAngle(prize) + includedAngle / 2)
      }deg)`
      // 本次抽中奖品名称
      this.prizeName = this.prizeList[prize].name
      // 开始抽奖
      this.isTake = true
      // 10秒出现抽奖按钮
      setTimeout(() => {
        this.isTake = false
      }, 10000)
    }
  }
}
</script>
<style lang="scss" scoped>
.index {
  text-align: center;
  .disc {
    margin: 0 auto;
    width: 500px;
    height: 500px;
    border-radius: 500px;
    position: relative;
    overflow: hidden;
    border: 1px solid;
  }

  @keyframes rotate {
    0% {
      transform: rotate(0deg);
    }
    100% {
      transform: var(--prizeAngle);
    }
  }
  .move {
    animation: rotate 10s 1 ease-in-out;
    transform: var(--prizeAngle);
  }

  .constellation-part {
    position: absolute;
    top: 0;
    right: 0;
    width: 50%;
    height: 50%;
    transform-origin: 0 100%;
  }
  .part {
    border-left: 1px solid #333;
    .text {
      text-align: center;
      position: relative;
      top: 120px;
    }
  }
  .triangle-up {
    width: 0;
    height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-bottom: 250px solid red;
    position: absolute;
    top: 0px;
    left: 246px;
  }
  .circular {
    width: 50px;
    height: 50px;
    border-radius: 50px;
    position: absolute;
    top: 225px;
    left: 226px;
    background: red;
  }

  .but {
    cursor: pointer;
    margin-top: 50px;
    width: 200px;
    height: 50px;
    border-style: none;
    background: #409eff;
    color: #fff;
    font-size: 16px;
    border-radius: 10px;
  }

  .prizeName {
    margin-top: 50px;
    color: red;
    font-size: 18px;
  }
}
</style>
Last Updated:
Contributors: 1102166242@qq.com, cjx