用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 = 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秒旋转一圈,永远循环 */
}