转盘抽奖的实现
如图效果展示:(因最近双十一,公司需求要给新老客服抽奖发福利,临时写的)

核心代码:
async rotating() { var type = 0; // 默认为 0 转盘转动 1 箭头和转盘都转动(暂且遗留) var during_time = 5; // 默认为1s var result_index = this.index-1; // 最终要旋转到哪一块,对应prize_list的下标 var result_angle = [190 ,139 ,88 ,37 ,-14,-65 ,-116] //这里是指定位置 var rand_circle = 6; // 附加多转几圈,2-3 var rotate_angle = 0; this.count++ if (type == 0) { // 转动盘子 var rateround=rand_circle * 360 + result_angle[result_index] //这里是(转盘算法) rateround是转动的距离 this.start_rtating是每次初始化转盘的位置 rotate_angle =this.start_rotating_degree + rateround -this.start_rotating_degree % 360 ; this.start_rotating_degree = rotate_angle; this.rotate_angle = "rotate(" + rotate_angle + "deg)"; this.lottery_ticket--; let that = this setTimeout(function() { that.game_over(); //提示框 },during_time * 1000 + 1500)// 延时,保证转盘转完 } },
做这个的时候,因为不是很理解转盘算法,第一次点击转盘可以转向指定位置,而第二次后都是有点偏移。这里主要靠 %360来取余,不管你指定位置多少,最终都是要 减去初始位 % 360。
推荐下载这个来看: https://gitee.com/goodSkz/lunckDraw
详细代码如下:
<template>
<!-- 新客户链接 -->
<div class="container">
<div class="draw-title">
<div class="draw-name">{{title}}</div>
</div>
<div class="lucky-wheel">
<div class="wheel-main">
<div class="wheel-pointer-box">
<div class="wheel-pointer" @click="rotate_handle()" :style="{transform:rotate_angle_pointer,transition:rotate_transition_pointer}"></div>
</div>
<div class="wheel-bg" :style="{transform:rotate_angle,transition:rotate_transition}">
<div class="prize-list">
<div class="prize-item" v-for="(item,index) in prize_list" :key="index">
<div class="prize-pic">
<!-- <img :src="item.prizeImg"> -->
</div>
<div class="prize-count">
<span v-if="item.gradeNum!=7">{{item.gradeName}}</span>
</div>
<div class="prize-text">
<span v-if="item.gradeNum!=7">{{item.prizeName}}</span>
</div>
<div class="prize-type">
<span v-if="item.gradeNum==7">{{item.gradeName}}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="main">
<div class="content">
<div class="lottery_ticket"></div>
<div class="scroll_bg">
<ul class="scroll_content" :style="{ top }">
<li v-for="(item,index) in WinList" :key="index" >
<el-row>
<el-col :span="6">{{index+1}}.{{item.phone | iphone}}</el-col>
<el-col :span="14">{{item.prizeGrade.prizeName}}</el-col>
<el-col :span="4">{{item.timeAgo}}</el-col>
</el-row>
</li>
</ul>
</div>
</div>
<div class="lotter_foot">
<span class="left" @click="getMyPrizeRecord">我的奖品 ></span>
<span class="right" @click="todrawRule">活动规则 ></span>
</div>
</div>
<!-- 输入手机号 -->
<div class="toast" v-show="toast_phoneNum">
<div class="toast-container">
<!-- <img :src="toast_pictrue" class="toast-picture"> -->
<div class="close" @click="close_phoneNum()"></div>
<div class="toast-title">
<span style="margin-right:10px;">手机号:</span>
<el-input size="small" style="width:60%;height:0.75rem;" v-model="phoneNum"></el-input>
</div>
<div class="toast-btn">
<div class="toast-cancel" @click="runDraw">确 认</div>
</div>
</div>
</div>
<div class="toast-mask" v-show="toast_phoneNum"></div>
<!-- 输入手机号 -->
<div class="toast" v-show="toast_myphonecontrol">
<div class="toast-container">
<!-- <img :src="toast_pictrue" class="toast-picture"> -->
<div class="close" @click="close_phoneNum()"></div>
<div class="toast-title">
<span style="margin-right:10px;">手机号:</span>
<el-input size="small" style="width:60%;height:0.75rem;" v-model="phoneNum"></el-input>
</div>
<div class="toast-btn">
<div class="toast-cancel" @click="runphone">确 认</div>
</div>
</div>
</div>
<div class="toast-mask" v-show="toast_myphonecontrol"></div>
<!-- 中奖提示 -->
<div class="toast" v-show="toast_control">
<div class="toast-container">
<!-- <img :src="toast_pictrue" class="toast-picture"> -->
<div class="close" @click="close_toast()"></div>
<div class="toast-title">
{{toast_title}}
</div>
<div class="toast-btn">
<div class="toast-cancel" @click="close_toast">确 认</div>
</div>
</div>
</div>
<div class="toast-mask" v-show="toast_control"></div>
<!-- 我的奖品 -->
<div class="toast" v-show="toast_mycontrol">
<div class="toast-container">
<!-- <img :src="toast_pictrue" class="toast-picture"> -->
<div class="close" @click="close_mytoast()"></div>
<div class="toast-title">
<div>抽中奖品列表</div>
<li v-for="(item,index) in mycontList" :key="index">
{{item.entities.gradeName}}——{{item.luckDrawTime | datetime('date')}}
</li>
</div>
<div class="toast-btn">
<div class="toast-cancel" @click="close_mytoast">确 认</div>
</div>
</div>
</div>
<div class="toast-mask" v-show="toast_mycontrol"></div>
</div>
</template>
<script>
import {getPrizeActivityById,getPrizeList,startRaffle,checkLuckyDraw,getMyPrizeRecord,getPrizeRecordTopTenList } from '../../api/getData';
export default {
data(){
return{
index:0,
angle:0,
toast_title:'',
start_rotating_degree: 0, //初始旋转角度
rotate_angle: 0, //将要旋转的角度
start_rotating_degree_pointer: 0, //指针初始旋转角度(360/分页)
rotate_angle_pointer: 0, //指针将要旋转的度数
rotate_transition: "transform 6s ease-in-out", //初始化选中的过度属性控制
rotate_transition_pointer: "transform 12s ease-in-out", //初始化指针过度属性控制
lottery_ticket:1,//抽奖次数
prize_list: [], //奖品列表
toast_control:false,
toast_mycontrol:false,
click_flag:true, //是否可以旋转抽奖
toast_phoneNum:false,
toast_myphonecontrol:false,
phoneNum:'',
WinList:[],//中奖名单
activeIndex: 0,
activityId:null, //用户id
mycontList:[],
title:''
}
},
watch:{
$route(){
console.log(this.$route.query.id)
console.log(this.$route.meta.title)
}
},
methods:{
async getPrizeList(){
const result = await getPrizeList({activityId:this.activityId})
if(result.code == 200){
this.prize_list = result.data
}else{
this.$toast({message:result.message ,position:'top'});
}
},
rotate_handle(){
this.phoneNum = ''
if(!this.phoneNum){
this.toast_phoneNum = true
}
},
async runDraw(){
if(this.phoneNum.length!=0){
var reg=/^1[3456789]\d{9}$/;
if(!reg.test(this.phoneNum)) return this.$toast({message:'请输入有效的手机号码' ,position:'top'});
const result = await checkLuckyDraw({phoneNumber:this.phoneNum,activityId:this.activityId})
if(result.code == 200){
//需要判断
const result = await startRaffle({phoneNumber:this.phoneNum,activityId:this.activityId})
if(result.code == 200){
this.index = result.data.gradeNum;
this.rotating()
}else{
this.$toast({message:result.message ,position:'top'});
}
this.toast_phoneNum = false
}else{
this.$toast({message:result.data ,position:'top'});
}
}else{
this.$toast({message:'手机号不能为空!' ,position:'top'});
}
},
async runphone(){
if(this.phoneNum.length!=0){
var reg=/^1[3456789]\d{9}$/;
if(!reg.test(this.phoneNum)) return this.$toast({message:'请输入有效的手机号码' ,position:'top'});
const result = await checkLuckyDraw({phoneNumber:this.phoneNum,activityId:this.activityId})
if(result.code == 500){
//需要判断
this.toast_myphonecontrol = false
this.toast_mycontrol = true
this.getMyPrizeRecord()
}else{
this.$toast({message:result.data ,position:'top'});
}
}else{
this.$toast({message:'手机号不能为空!' ,position:'top'});
}
},
async rotating() {
var type = 0; // 默认为 0 转盘转动 1 箭头和转盘都转动(暂且遗留)
var during_time = 5; // 默认为1s
var result_index = this.index-1; // 最终要旋转到哪一块,对应prize_list的下标
var result_angle = [190 ,139 ,88 ,37 ,-14,-65 ,-116]
var rand_circle = 6; // 附加多转几圈,2-3
var rotate_angle = 0;
this.count++
if (type == 0) {
// 转动盘子
var rateround=rand_circle * 360 + result_angle[result_index]
rotate_angle =this.start_rotating_degree + rateround
-this.start_rotating_degree % 360 ;
this.start_rotating_degree = rotate_angle;
this.rotate_angle = "rotate(" + rotate_angle + "deg)";
this.lottery_ticket--;
let that = this
setTimeout(function() {
that.game_over(); //提示框
},during_time * 1000 + 1500)// 延时,保证转盘转完
}
},
game_over() {
if(this.prize_list[this.index-1].prizeName=='') return
this.toast_control = true;
if(this.prize_list[this.index-1].gradeNum ==7){
return this.toast_title = "抽中结果为:"+this.prize_list[this.index-1].gradeName
}else{
return this.toast_title = "抽中结果为:"+this.prize_list[this.index-1].prizeName+this.prize_list[this.index-1].gradeName
}
},
async getMyPrizeRecord(){
if(this.phoneNum){
const result = await getMyPrizeRecord({phoneNumber:this.phoneNum,activityId:this.activityId})
if(result.code == 200){
this.toast_mycontrol = true;
this.mycontList = result.data
}else{
this.$toast({message:result.message ,position:'top'});
}
}else{
this.toast_myphonecontrol = true
}
},
close_mytoast(){
this.toast_mycontrol = false
},
//关闭弹窗
close_toast() {
this.toast_control = false;
this.getPrizeRecordTopTenList()
},
//关闭手机号录入
close_phoneNum(){
this.toast_phoneNum = false;
this.toast_myphonecontrol= false
},
todrawRule(){
this.$router.push('/newdrawRule?id='+this.$utils.getUrlKey('id'))
},
async getPrizeRecordTopTenList(){
const result = await getPrizeRecordTopTenList({activityId:this.activityId})
if(result.code == 200){
this.WinList = result.data
}else{
this.$toast({message:result.message ,position:'top'});
}
},
async getPrizeActivityById(){
const result = await getPrizeActivityById({id:this.$utils.getUrlKey('id')})
if(result.code == 200){
this.activityId = result.data.id
this.title = result.data.name
document.title = this.title
this.getPrizeList()
this.getPrizeRecordTopTenList()
}else{
this.$toast({message:result.message ,position:'top'});
}
}
},
mounted(){
let _this = this;
let timer = setInterval(function(){
if(_this.WinList.length){
let b = _this.WinList.shift()
_this.WinList.push(b)
}
}, 2000);
this.getPrizeActivityById()
},
computed:{
top() {
return -this.activeIndex * 1.35 + 'rem'
}
}
}
</script>
<style lang="less">
.container {
width: 100%;
background: url("../../assets/img/draw/bgdraw.jpg") no-repeat;
background-size: 100% ;
.draw-title{
position: absolute;
top: 1.225rem;
left: 50%;
transform: translateX(-50%);
background-size: 100% ;
width: 90%;
height: 3.5625rem;
.draw-name{
margin: auto;
color: rgb(110,20,54);
font-weight: 600;
background: rgb(255,228,1);
border: .2rem solid rgb(254, 118, 0);
border-radius: .2rem;
padding: 0.4rem ;
text-align: center;
}
}
.lucky-wheel{
width: 100%;
height: 42.5625rem;
padding-top: 23.5625rem;
.wheel-main {
display: flex;
align-items: center;
justify-content: center;
position: relative;
.wheel-pointer-box {
position: absolute;
top: 50%;
left: 49.2%;
z-index: 100;
transform: translate(-50%, -50%);
width: 18.3125rem;
height: 18.3125rem;
background: url("../../assets/img/draw/drawtab.png") no-repeat center top;
background-size: 100%;
.wheel-pointer {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 15.3125rem;
height: 15.3125rem;
background: url("../../assets/img/draw/drawbtn.png") no-repeat;
background-size: 100% 100%;
transform-origin: center 60%;
}
}
.wheel-bg {
width: 18.4375rem;
height: 18.4375rem;
background: url("../../assets/img/draw/turntable.png") no-repeat center top;
background-size: 100%;
transform: rotate(12deg);
font-weight: 500;
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
transition: transform 3s ease;
div {
text-align: center;
}
.prize-list{
width: 100%;
height: 100%;
position: relative;
.prize-item {
font-size: 0.45rem;
position: absolute;
top: 10%;
left: 39%;
width: 3.9rem;
height: 14.5rem;
padding-top: 0.5rem;
margin: auto;
z-index: 2;
line-height: 0.85rem;
.prize-pic img {
width: 4.0625rem;
height: 2.5rem;
margin-top: 1.5625rem;
}
.prize-count{
font-size: 0.55rem;
}
.prize-text {
font-size: 0.55rem;
}
.prize-type {
font-size: 0.55rem;
padding-top: 1rem;
}
}
.prize-item:first-child {
transform: rotate(-194deg);
}
.prize-item:nth-child(2) {
transform: rotate(-140deg);
}
.prize-item:nth-child(3) {
transform: rotate(-88deg);
}
.prize-item:nth-child(4) {
transform: rotate(-38deg);
}
.prize-item:nth-child(5) {
transform: rotate(13deg);
}
.prize-item:nth-child(6) {
transform: rotate(63deg);
}
.prize-item:nth-child(7) {
transform: rotate(114deg);
}
}
}
}
}
.main{
position: relative;
width: 100%;
padding-bottom: 1.6875rem;
.content {
position: relative;
top: 0.175rem;
left: 0;
background: url("../../assets/img/draw/winner.png") no-repeat center top;
background-size: 100%;
width: 100%;
height: 12.68rem;
margin: auto;
font-size: 1.125rem;
color: #ffeb39;
text-align: center;
.lottery_ticket{
height: 4rem;
}
.scroll_bg{
height: 5.5rem;
margin-top: 1.43rem;
overflow: hidden;
position: relative;
.scroll_content{
position: relative;
transition: top 0.5s;
-webkit-transition: top 0.5s;
}
ul{
margin:0rem 2rem 1rem;
font-size: .55rem;
line-height: 1.35rem;
// height: 30.14rem;
// overflow: scroll;
}
li{
line-height:1.35rem;
}
}
}
.lotter_foot{
color: #fff;
z-index: 999;
width: 90%;
margin:1.8rem auto;
font-size: 0.75rem;
}
}
.toast {
position: fixed;
top: 50%;
left: 50%;
z-index: 20000;
transform: translate(-50%, -50%);
width: 15.4375rem;
background: #fff;
border-radius: 0.3125rem;
padding: 0.3125rem;
background-color: rgb(252, 244, 224);
.toast-container {
position: relative;
width: 100%;
height: 100%;
border: 1px dotted #fccc6e;
.close {
position: absolute;
top: -0.9375rem;
right: -0.9375rem;
width: 2rem;
height: 2rem;
background: url("../../assets/img/draw/close_store.png") no-repeat center top;
background-size: 100%;
}
.toast-title {
padding: 2.8125rem 0;
font-size: 0.75rem;
color: #fc7939;
text-align: center;
}
.toast-btn {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0.9375rem;
div {
background-image: -moz-linear-gradient(-18deg,rgb(242, 148, 85) 0%,rgb(252, 124, 88) 51%, rgb(252, 124, 88) 99%);
background-image: -ms-linear-gradient(-18deg,rgb(242, 148, 85) 0%,rgb(252, 124, 88) 51%,rgb(252, 124, 88) 99%);
background-image: -webkit-linear-gradient(-18deg,rgb(242, 148, 85) 0%,rgb(252, 124, 88) 51%,rgb(252, 124, 88) 99%);
box-shadow: 0px 4px 0px 0px rgba(174, 34, 5, 0.7);
width: 4.6875rem;
height: 1.875rem;
border-radius: 1.875rem;
text-align: center;
line-height: 1.875rem;
color: #fff;
}
}
}
}
.toast-mask {
position: fixed;
top: 0;
left: 0;
background: rgba(0, 0, 0, 0.6);
z-index: 1000;
width: 100%;
height: 100%;
}
.van-toast van-toast--top van-toast--text{
z-index: 9999;
}
}
</style>

浙公网安备 33010602011771号