一、 最近有个需求在前端做抽奖的业务,然后查了些资料,实现了一下。效果图如下:
二、实现原理:CSS的transitions:rotate属性,传入度数即可旋转;调整中奖概率也写的几乎人人可以看懂的流程,也比较巧妙地实现了对概率的控制,称不上多厉害的算法,但确实达到了想要效果。
1.在html中:
<template>
<div id="bg">
<img @click="go" class="pointer" src="./pointer.png" alt="pointer">
<img ref="turntable" class="turntable" src="./turntable.png" alt="turntable">
<h1>你的抽奖次数为:{
{goTimes}}</h1>
</div>
</template>
2.在js中:
export default {
data(){
return {
isGo: false, //是否正在执行动画
oTurntable:'', //执行动画的对象
randomDeg: 0, //即将旋转的度数
lastDeg:0, //上一次旋转的度数
goTimes:5 //抽奖次数
}
},
methods:{
//这里是调自己的后端接口
selectPersonResultSum(){
var vm = this;
vm.userId = localStorage.getItem("userId");
vm.$axios
.get("/personResult/selectPersonResultSum/" + vm.userId)
.then(function (response) {
if (response.data.code === "0000") {
vm.num=response.data.data.num;
if(vm.num%10==0){
vm.goTimes=1;
}
} else if (response.data.code === "1111") {
vm.$message({
message: "",
type: "info",
});
}
});
},
go(){
if(!this.isGo && this.goTimes >0){ this.getNumber() }
else if(!this.isGo && this.goTimes <=0){ alert('抱歉您的抽奖次数用完了') }
else return //表明正在抽奖,未结束 点击无效
},
getRandom(n,m){ //该方法产生[n,m]之间随机数
let result = Math.floor(Math.floor(Math.random()*(m-n+1)+n))
return result;
},
getNumber(){
/*
调整中奖概率(让每次旋转前指针都在初始位置,这样才准确)
想转到第一项,需要转:360/7*0 + 360/7/2; --->该项为超级大奖奖项
想转到第二项,需要转:360/7*1 + 360/7/2;
想转到第三项,需要转:360/7*2 + 360/7/2;
想转到第四项,需要转:360/7*3 + 360/7/2;
想转到第五项,需要转:360/7*4 + 360/7/2;
想转到第六项,需要转:360/7*5 + 360/7/2;
想转到第七项,需要转:360/7*6 + 360/7/2; --->该项为未中奖项
*/
let number = this.getRandom(0,100)
let caseNum = ''
if(number === 0){
caseNum = 0 //粗略概率为1%
}else if(number > 0 && number <5){
caseNum = 1 //粗略概率为5%
}else if(number >= 5 && number <10){
caseNum = 2 //粗略概率为5%
}else if(number >= 10 && number <15){
caseNum = 3 //粗略概率为5%
}else if(number >= 15 && number <20){
caseNum = 4 //粗略概率为5%
}else if(number >= 20 && number <25){
caseNum = 5 //粗略概率为5%
}else{
caseNum = 6 //粗略概率为75%
}
switch (caseNum) {
case 0:
this.ratating((360 / 7) * 0 + 360 / 7 / 2, "5");
break;
case 1:
this.ratating((360 / 7) * 1 + 360 / 7 / 2, "1");
break;
case 2:
this.ratating((360 / 7) * 2 + 360 / 7 / 2, "4");
break;
case 3:
this.ratating((360 / 7) * 3 + 360 / 7 / 2, "1");
break;
case 4:
this.ratating((360 / 7) * 4 + 360 / 7 / 2, "2");
break;
case 5:
this.ratating((360 / 7) * 5 + 360 / 7 / 2, "3");
break;
default:
this.ratating((360 / 7) * 6 + 360 / 7 / 2, "1");
break;
}
},
ratating(deg,text){
this.goTimes --
this.isGo = true
let times = this.getRandom(3,6)//圈数(从用户体验角度考虑,设计随机转3-6圈。最少3圈,最多6圈)
this.randomDeg = deg + 360 * times //记录这次要旋转的度数(传来的度数+圈数)
let realDeg = (360 - this.lastDeg % 360) + this.lastDeg + this.randomDeg
/*上次指针离初始状态的度数 + 上次的度数 + 这次要旋转的度数
(这样的目的是为了每次旋转都从原点开始,保证数据准确)*/
this.oTurntable.style.transform = `rotate(${realDeg}deg)`;
setTimeout(() => {
this.isGo = false
console.log(`以原点为基准共旋转了${this.randomDeg}度,
以一圈为基准相对旋转了${this.randomDeg % 360}度,最终结果为${text}`)
this.lastDeg = realDeg //把这次度数存储起来,方便下一次获取
},4000) //4000ms为css里面写的执行动画的时间
},
},
mounted(){
this.oTurntable = this.$refs.turntable
}
}
</script>
3.在css样式中:
<style>
#bg {
width: 650px;
height: 600px;
margin: 0 auto;
background: url('./turntable-bg.jpg') no-repeat;
position: relative;
}
.pointer{
position: absolute;
z-index: 10;
top: 155px;
left: 247px;
}
.turntable{
position: absolute;
z-index: 5;
top: 60px;
left: 116px;
transition: all 4s; /*动画执行时间为4s */
}
</style>
三、总结
大家细心读完代码程序会发现我们每次旋转都加上了上一次旋转的度数,这是为什么:
----如果不加上上一次旋转的度数,直接用这次的旋转度数,会有问题,比如:我第一次旋转刚好旋转了1000度,正常转,没问题,但我第二次旋转抽中旋转500度,那么,旋转对象会在第一次的基础上往回退500度,因为他每次旋转都是以0参考对象,到时候一会逆时针转一会顺时针转,很难看,抽奖的人到时候都晕了,因此必须在上一次旋转的基础上再加上这次旋转的度数;this.lastDeg % 360是获得余数,也就是排除圈数,获得离原点的度数。