用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)’ }”
- v-for=“(item, index) in prize”:这表示你正在循环渲染 prize 数组中的每个项目,每个项目都有一个
item 和其在数组中的 index。这个 index 就是每个奖品项的位置。 - index * 36:每个项目的旋转角度是基于其索引计算的。这里每个项目的旋转角度是 36 度(360 ÷ 10 = 36),因为假设转盘有 10 项。通过将 index 乘以 36,每个项目将被均匀地分布在转盘上。
- :style=“{ transform: ‘rotate(’ + (index * 36) + ‘deg)’ }”:这段代码为每个
lucky-wheel-prize-item 动态添加一个内联的 style 属性,指定它的 transform CSS 样式。这个
transform 样式使用 rotate() 函数来旋转每个奖品项。 - 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 = trueapi.getRandom().then(res => {console.log(res);isStart.value = falsewinningIndex.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 = falseisStart.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 = trueconsole.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 = falseisSpinning.value=falseconsole.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=0winner.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秒旋转一圈,永远循环 */}