vue实现大转盘抽奖

发布于:2025-04-08 ⋅ 阅读:(34) ⋅ 点赞:(0)

用vue实现一个简单的大转盘抽奖案例

大转盘

一 转盘布局

<div class="lucky-wheel-content">
            <div class="lucky-wheel-prize" :style="wheelStyle" :class="isStart ? 'animated-icon' : ''"
              @transitionend="onWheelTransitionEnd">
              <div class="lucky-wheel-prize-item" v-for="(item, index) in prize" :key="item.id"
                :style="{ transform: 'rotate(' + (index * 36) + 'deg)' }">
                <span>{{ item.reward }}</span>
                <img :src="item.img" alt="">
              </div>
            </div>
            <div class="lucky-wheel-btn" @click="spinWheel">RODAR</div>
            <img class="lucky-wheel-poiner" src="/img/activity/zphd_zz_s1.avif" />
          </div>

1.1 :style=“{ transform: ‘rotate(’ + (index * 36) + ‘deg)’ }”

  1. v-for=“(item, index) in prize”:这表示你正在循环渲染 prize 数组中的每个项目,每个项目都有一个
    item 和其在数组中的 index。这个 index 就是每个奖品项的位置。
  2. index * 36:每个项目的旋转角度是基于其索引计算的。这里每个项目的旋转角度是 36 度(360 ÷ 10 = 36),因为假设转盘有 10 项。通过将 index 乘以 36,每个项目将被均匀地分布在转盘上。
  3. :style=“{ transform: ‘rotate(’ + (index * 36) + ‘deg)’ }”:这段代码为每个
    lucky-wheel-prize-item 动态添加一个内联的 style 属性,指定它的 transform CSS 样式。这个
    transform 样式使用 rotate() 函数来旋转每个奖品项。
  4. rotate(index * 36):基于 index 计算出每个项目的旋转角度。比如:
  • 对于第一个项目(index = 0),旋转角度是 0deg。
  • 对于第二个项目(index = 1),旋转角度是 36deg。
  • 对于第三个项目(index = 2),旋转角度是 72deg,以此类推。
    效果:通过这种方式,每个 lucky-wheel-prize-item 将会根据它在转盘上的位置进行旋转,使它们均匀分布在 360 度的转盘上。这样,整个转盘就呈现出一个环形布局,每个项按顺序排列。

在这里插入图片描述

计算转盘旋转的样式

通过计算属性

const wheelStyle = computed(() => {
  return {
    transform: `rotate(${rotationAngle.value}deg)`,
  }
});
  • rotationAngle 是某个响应式变量(可能是通过其他逻辑计算出来的旋转角度),它的值决定了转盘旋转的角度。
  • 当 rotationAngle.value 发生变化时,computed 属性会自动重新计算并返回新的样式对象,其中
    rotate(${rotationAngle.value}deg) 的角度会根据最新的 rotationAngle.value 动态更新。
  • 然后,这个 wheelStyle 对象可以应用到某个 DOM 元素上,通常是一个转盘的 style 属性,以便旋转转盘。
<div :style="wheelStyle">
  <!-- 转盘内容 -->
</div>

二 转盘逻辑

2.1 这里最重要的是转盘总的旋转角度的设置

  spins.value += 5;  // 开始的旋转圈数

  // 后端返回中奖项的逻辑  随机数
  winningIndex.value = Math.floor(Math.random() * totalItems.value);

  console.log("中奖项:" + winningIndex.value);

  const anglePerItem = 360 / totalItems.value;

  let randomAngle = 360 - (winningIndex.value * anglePerItem); // 计算旋转到中奖的那一项
  let totalAngle = spins.value * 360 + randomAngle; // 总的旋转角度

  console.log("总的旋转角度:" + totalAngle);

  // 设置旋转角度
  rotationAngle.value = totalAngle;
  • let randomAngle = 360 - (winningIndex.value * anglePerItem);
    randomAngle 是根据中奖项索引计算出的角度。winningIndex.value 乘以 anglePerItem 得到当前中奖项相对于起始位置的角度,360 - … 计算出从当前起始位置到中奖项的逆时针旋转角度。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.2 在请求后端获取数据时,保持转动效果

  • :class=“isStart ? ‘animated-icon’ : ‘’”
    主要是通过加上这个类名来保持这个效果
  spins.value += 5;
    isStart.value = true
  api.getRandom().then(res => {
    console.log(res);
    isStart.value = false
    winningIndex.value = res.data
    // 每个奖品的角度
    const anglePerItem = 360 / totalItems.value;

    // 计算中奖项的角度
    let randomAngle = 360 - (winningIndex.value * anglePerItem);
    let totalAngle = spins.value * 360 + randomAngle; // 总的旋转角度

    // 设置旋转角度
    rotationAngle.value = totalAngle;
  }).finally(() => {
    console.log('中奖项是:' + prize[winningIndex.value].reward);
    isSpinning.value = false
    isStart.value = false
  })

api.getRandom()这个是我本地java写的一个接口,模拟返回一个中奖的索引,同时该接口延迟5s返回数据

   @GetMapping("/random")
    public Result<Integer> getRandom(){
        try {
            // 延迟5秒
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // 处理异常
            e.printStackTrace();
        }

        Random random = new Random();
        Integer integer = random.nextInt(10); // 生成0到10之间的随机整数

        return Result.success(integer); // 返回成功结果
    }

大转盘1

三 完整代码

 <div class="lucky-wheel-content">
            <div class="lucky-wheel-prize" :style="wheelStyle" :class="isStart ? 'animated-icon' : ''"
              @transitionend="onWheelTransitionEnd">
              <div class="lucky-wheel-prize-item" v-for="(item, index) in prize" :key="item.id"
                :style="{ transform: 'rotate(' + (index * 36) + 'deg)' }">
                <span>{{ item.reward }}</span>
                <img :src="item.img" alt="">
              </div>
            </div>
            <div class="lucky-wheel-btn" @click="spinWheel">RODAR</div>
            <img class="lucky-wheel-poiner" src="/img/activity/zphd_zz_s1.avif" />
          </div>
const prize = [
  {
    id: 1,
    reward: '0,05',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 2,
    reward: '1,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 3,
    reward: '2,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 4,
    reward: '3,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 5,
    reward: '4,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 6,
    reward: '5,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 7,
    reward: '15,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 8,
    reward: '25,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 9,
    reward: '35,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }, {
    id: 10,
    reward: '75,00',
    img: '/img/activity/img_zphdjp_s1.png'
  }
]
const totalItems = ref(prize.length); // 总共有多少个项
const winningIndex = ref(0); // 假设中奖项的索引(从0开始)
const rotationAngle = ref(0); // 当前旋转角度
const isSpinning = ref(false)
const spins = ref(0) //转盘转5圈
const winner = ref(null); // 中奖项
const number = ref(0)  //记录抽奖次数
const isFirst = ref(0)
const isStart = ref(false)
// 计算转盘旋转的样式
const wheelStyle = computed(() => {
    return {
      transform: `rotate(${rotationAngle.value}deg)`,
    }
  });

// 执行旋转转盘的操作
const spinWheel = () => {
  if (isSpinning.value) {
    return
  }
  number.value++;
  isStart.value = true
  console.log("当前抽奖次数:" + number.value);

  isSpinning.value = true; // 开始旋转

  winner.value = null; // 重置中奖项
  winningIndex.value = 0; // 重置中奖索引
  rotationAngle.value = 0; // 重置旋转角度
  spins.value += 5;
  // api.getRandom().then(res => {
  //   console.log(res);
  //     isStart.value = false
  //   winningIndex.value = res.data
  //   // 每个奖品的角度
  //   const anglePerItem = 360 / totalItems.value;

  //   // 计算中奖项的角度
  //   let randomAngle = 360 - (winningIndex.value * anglePerItem);
  //   let totalAngle = spins.value * 360 + randomAngle; // 总的旋转角度

  //   // 设置旋转角度
  //   rotationAngle.value = totalAngle;
  // }).finally(()=>{
  //   // console.log('中奖项是:'+prize[winningIndex.value].reward );
  //   isSpinning.value=false
  // })

    // 后端返回中奖项的逻辑  随机数
    winningIndex.value = Math.floor(Math.random() * totalItems.value);

    // 假设每次转盘转5圈
    // spins.value += 5;

    setTimeout(()=>{
      isStart.value = false
      isSpinning.value=false
      console.log('中奖项是:'+prize[winningIndex.value].reward );
    },2000)

    // 每个奖品的角度
    const anglePerItem = 360 / totalItems.value;

    // 计算中奖项的角度
    let randomAngle = 360 - (winningIndex.value * anglePerItem);
    let totalAngle = spins.value * 360 + randomAngle; // 总的旋转角度

    // 设置旋转角度
    rotationAngle.value = totalAngle;
};


// 监听转盘动画结束事件
const onWheelTransitionEnd = () => {
  isSpinning.value = false; // 旋转结束
  // rotationAngle.value=0
  winner.value = winningIndex.value; // 显示中奖项
  // console.log(winner.value, '123');

  // console.log(rotationAngle.value, '旋转角度');

  console.log('中奖项是:'+prize[winner.value].reward );

};
.lucky-wheel {

        .lucky-wheel-content {
          width: 15rem;
          height: 15rem;
          background-image: url('/img/activity/zphd_bj_s1.avif');
          background-size: 100% 100%;
          margin: .4rem auto .4rem auto;
          position: relative;
          transition: transform 5s ease-in-out;

          .lucky-wheel-prize {
            position: absolute;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
            transition: transform 4s ease-out;
            /* 控制旋转动画 */

            .lucky-wheel-prize-item {
              position: absolute;
              top: 0;
              right: 0;
              bottom: 0;
              left: 0;
              z-index: 10;
              width: 2rem;
              margin: auto;
              height: 100%;
              color: #ffff;
              font-size: .22rem;
              text-align: center;
              display: flex;
              align-items: center;
              flex-direction: column;

              &>img {
                width: 2rem;
                height: 2rem;
              }

              &>span {
                display: -webkit-box;
                overflow: hidden;
                text-overflow: ellipsis;
                vertical-align: middle;
                -webkit-line-clamp: 2;
                -webkit-box-orient: vertical;
                font-size: .6rem;
                word-break: break-all;
                height: 2.8rem;
                line-height: 2.8rem;

              }
            }

          }

          @keyframes spin {
            0% {
              transform: rotate(0deg);
              /* 开始时从0度 */
            }

            100% {
              transform: rotate(360deg);
              /* 结束时旋转一圈 */
            }
          }

          /* 应用动画 */
          .animated-icon {
            display: inline-block;
            animation: spin 1s linear infinite;
            /* 1秒旋转一圈,永远循环 */
          }

网站公告

今日签到

点亮在社区的每一天
去签到