用vuejs撸一个抽奖组件
2020/1/23 11:06:27
本文主要是介绍用vuejs撸一个抽奖组件,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
新年将至,先提前祝大家新年快乐万事如意,想必很多人已经开过了一个记忆深刻或者毫无趣味的年会了,那么,迟来的抽奖组件来了,233333333
先来一张效果图,哈哈,不是什么年会大抽奖,只是一个跑马灯抽奖组件,233333,明年年会前,一定写一篇年会用的抽奖data
先来看看要使用到的一些data
data () { return { bgStatus: true, // 控制抽奖边框的闪烁 bgInterval: '', // 存放背景切换的setInterval,方便组件销毁时清除任务 prizeList: [], // 用于渲染的奖品列表 idList: [], // 用于存放奖品id的数组 cur: undefined, // 当前步对应的项 speed: 100, // 速度 couldClick: true // 控制抽奖按钮可点击状态 } } 复制代码
先看看data的一个结构,用到的变量注释了,后面如果有疑问的可以到这边来看
template
看下我们的template结构
<template> <div class="md-div"> <!-----------跑马灯-------------> <div class="md-bd-bg" :class="bgStatus ? 'md-bd-bg1' : 'md-bd-bg2'"> <!----------抽奖容器--------------> <div class="item-out"> <!----------循环奖项--------------> <div class="prize-item flex-column flex-just-center flex-align-center" v-for="k in prizeList" :key="k.prizeid" :class="k.prizeid === cur ? 'prize-y' : 'prize-n'" > <div class="prize-img flex-row flex-just-end flex-align-center"> <img :src="`https://${k.photo}`" alt=""> </div> <!-- <div class="prize-word">{{k.prize_name}}</div>--> </div> <!-----------抽奖按钮-------------> <div class="prize-btn" @click="doPrize" :class="couldClick ? '' : 'refuse-click'"></div> </div> </div> </div> </template> 复制代码
分析一下结构,我们给了一个外层(class=md-bd-bg),用作外层灯光的闪烁控制,这里很简单,我们需要的只是两张图几乎一样的背景图
无视马赛克,我们只需要这样的外边框,另外一张图也是这样,只需要黄灯和白灯切换位置,即可做出闪烁的效果.md-bd-bg1{background-image: url("../../statics/tbIcon/shine1.png")} .md-bd-bg2{background-image: url("../../statics/tbIcon/shine2.png")} .md-bd-bg{ width: 100%; height: 100%; background-size: 100% 100%; padding: 6vw; } 复制代码
这就是外边框的样式,md-bd-bg1和md-bd-bg2分别对应了两个状态,下一步就是要让他动起来
controlBg () { // 背景变换函数 let vm = this vm.bgInterval = setInterval(() => { vm.bgStatus = !vm.bgStatus }, 500) } 复制代码
methods当中的一个方法,我们用bgInterval存放了一个setInterval,之所以要用一个变量存储,是为了在清除组件的时候清除事件
正题来啦
终于到了我们正儿八经的跑马灯抽奖了,先分析结构
<!----------循环奖项--------------> <div class="prize-item flex-column flex-just-center flex-align-center" v-for="k in prizeList" :key="k.prizeid" :class="k.prizeid === cur ? 'prize-y' : 'prize-n'" > <div class="prize-img flex-row flex-just-end flex-align-center"> <img :src="`https://${k.photo}`" alt=""> </div> <!-- <div class="prize-word">{{k.prize_name}}</div>--> </div> 复制代码
使用v-for将请求到的奖品数组循环渲染
.prize-y{background-image: url("../../statics/tbIcon/prize_y.png");} .prize-n{background-image: url("../../statics/tbIcon/prize_n.png")} .prize-item:nth-of-type(1){top: .1vw;left: .1vw;} .prize-item:nth-of-type(2){top: .1vw;left: 27.35vw;} .prize-item:nth-of-type(3){top: .1vw;left: 54.6vw;} .prize-item:nth-of-type(4){top: 27.35vw;left: 54.6vw;} .prize-item:nth-of-type(5){top: 54.6vw;left: 54.6vw;} .prize-item:nth-of-type(6){top: 54.6vw;left: 27.35vw;} .prize-item:nth-of-type(7){top: 54.6vw;left: .1vw;} .prize-item:nth-of-type(8){top: 27.35vw;left: .1vw;} 复制代码
这里用到了vw单位,vw和vh、vmin、vmax这几个,不说了,用了都说香,上面的代码将奖项摆放成了一个圈,留下了按钮的空间,下面是点击按钮的css
.prize-btn{width: 27vw;height: 27vw;background-size: 100% 100%;position: absolute;background-image: url("../../statics/tbIcon/prize-btn.png");top:27.35vw;left:27.35vw;} 复制代码
好啦,东西摆放好了,我们愉快地开始写逻辑吧
点击抽奖
看看我们的抽奖按钮
<template> <div class="prize-btn" @click="doPrize" :class="couldClick ? '' : 'refuse-click'"></div> </template> /*------------------禁止点击-------------*/ .refuse-click{filter: grayscale(10%);} || \||/ \/ doPrize () { let vm = this if (vm.couldClick) { vm.couldClick = false // 禁止第一次抽奖没执行完又进行下一次抽奖 vm.$axios(urls.doPrize, {}).then(res => { let code = res.code if (code === 'success') { vm.prizeAnimate(res) } else { vm.$q.notify({ message: res.msg }) } }) } } 复制代码
这里给了按钮一个动态class,让它在可点击的时候是设计图上的原色,不可点击的时候则加上灰色的滤镜,讲道理,css的filter也是真的香,有兴趣的同学可以去看看,可以看到点击了抽奖按钮后,并不是直接执行prizeAnimate,而是先向服务请求了抽奖结果,也就是说,后面的动画都是多余的,你点击的时候已经决定了你的中奖结果,所以我们不写动画了,就这样就可以直接用了
继续
开个玩笑 粘上我的抽奖动画代码
prizeAnimate (result) { let vm = this vm.cur = undefined let num = vm.idList.indexOf(Number(result.prizeid)) let len = vm.idList.length let allSteps = Math.floor(Math.random() * 3 + 2) * len + num // 总共要走的步数 2~5圈 let a = 0 function myInt () { setTimeout(() => { if (a <= allSteps) { // a是已经跳动的步数 if (allSteps - a < len * 2 && allSteps - a >= len) { // 进入倒数第二圈,增加延时 vm.speed = 200 } else if (allSteps - a < len) { // 进入倒数第一圈,再次增加延时 vm.speed = 400 } vm.cur = vm.idList[a % vm.idList.length] // 当前跳至的奖项可以用步数除以奖品数组长度求余即可 a++ // 执行完跳动后记得将已跳动步数加1 myInt() // 递归调用 } else { // 终于跳完了 vm.couldClick = true // 解除对按钮的限制 vm.speed = 100 // 将速度还原 // ------- // 抽奖动画执行完接下来的操作,比如说弹窗通知中奖 } }, vm.speed) } myInt() } } 复制代码
拿到了抽奖结果,第一步,我们将cur清空了,接着取出中奖结果在奖品数组里的index值,也就是num 此处的allSteps用来生成总共需要走的步数,为了让动画尽可能看起来‘我们是真的用动画抽了的噢’,所以设置了2到5圈再加上前面求到的index值, 那么现在我们就知道了总共要走的步数
那么走起
这里我声明了myInt函数用作递归执行setTimeout的函数体,这样做的目的,首先,我们的抽奖是有动画的,所以速度肯定不能匀速,如果使用setInterval,那么变更speed并不会改变动画的跳动速度,其次嘛,setInterval其实也可以用,但是你得写三个,一个快速的,一个中速的,一个低速的,然后挨个执行,你看我大setTimeout递归调用,不香吗,代码量不少吗!哈哈,本来想再解释一下这段函数,但是感觉都写在备注里了,那就这样吧,我要收拾收拾回家过年了,886~~
整个组件奉上
<template> <div class="md-div"> <div class="md-bd-bg" :class="bgStatus ? 'md-bd-bg1' : 'md-bd-bg2'"> <div class="item-out"> <div class="prize-item flex-column flex-just-center flex-align-center" v-for="k in prizeList" :key="k.prizeid" :class="k.prizeid === cur ? 'prize-y' : 'prize-n'" > <div class="prize-img flex-row flex-just-end flex-align-center"> <img :src="`https://${k.photo}`" alt=""> </div> <!-- <div class="prize-word">{{k.prize_name}}</div>--> </div> <!-----------抽奖按钮-------------> <div class="prize-btn" @click="doPrize" :class="couldClick ? '' : 'refuse-click'"></div> </div> </div> </div> </template> <script> import urls from 'src/api/urls' export default { name: 'module', data () { return { bgStatus: true, bgInterval: '', prizeList: [], // 奖品列表 idList: [], cur: undefined, // 当前帧对应的项 speed: 100, couldClick: true // 抽奖按钮可点击 } }, created () { let vm = this vm.controlBg() // 开启背景变换 vm.queryList() // 请求奖项 }, methods: { controlBg () { // 背景变换函数 let vm = this vm.bgInterval = setInterval(() => { vm.bgStatus = !vm.bgStatus }, 500) }, queryList () { let vm = this vm.$axios(urls.getPrizeList, {}).then(res => { let code = res.code if (code === 'success') { // console.log(res) for (let k in res.lottery_prize) { vm.idList.push(res.lottery_prize[k].prizeid) } vm.prizeList = res.lottery_prize vm.$emit('subNotice', res) } else { vm.$router.go(-1) } }) }, doPrize () { let vm = this if (vm.couldClick) { vm.couldClick = false // 禁止下次立即执行 vm.$axios(urls.doPrize, {}).then(res => { let code = res.code if (code === 'success') { vm.prizeAnimate(res) } else { vm.$q.notify({ message: res.msg }) } }) } }, prizeAnimate (result) { let vm = this vm.cur = undefined let num = vm.idList.indexOf(Number(result.prizeid)) let len = vm.idList.length let allStamps = Math.floor(Math.random() * 3 + 2) * len + num // 总共要走的步数 2~5圈 let a = 0 function myInt () { setTimeout(() => { if (a <= allStamps) { if (allStamps - a < len * 2 && allStamps - a >= len) { vm.speed = 200 } else if (allStamps - a < len) { vm.speed = 400 } vm.cur = vm.idList[a % vm.idList.length] a++ myInt() } else { vm.couldClick = true vm.$emit('subDc', result) vm.speed = 100 } }, vm.speed) } myInt() } } } </script> <style scoped> .md-div{ width: 94vw; height: 94vw; position: relative; margin: 3vw; } .md-bd-bg1{background-image: url("../../statics/tbIcon/shine1.png")} .md-bd-bg2{background-image: url("../../statics/tbIcon/shine2.png")} .md-bd-bg{ width: 100%; height: 100%; background-size: 100% 100%; padding: 6vw; } .item-out{width: 100%;height: 100%;position: relative;} .prize-item{ width: 27vw; height: 27vw; background-size: 100% 100%; position: absolute; } .prize-btn{width: 27vw;height: 27vw;background-size: 100% 100%;position: absolute;background-image: url("../../statics/tbIcon/prize-btn.png");top:27.35vw;left:27.35vw;} .prize-img{width: 70%;height: 40%;} .prize-img img{width: 100%;height: auto;} .prize-word{color: #832909;font-size: 1.4rem;} .prize-y{background-image: url("../../statics/tbIcon/prize_y.png");} .prize-n{background-image: url("../../statics/tbIcon/prize_n.png")} .prize-item:nth-of-type(1){top: .1vw;left: .1vw;} .prize-item:nth-of-type(2){top: .1vw;left: 27.35vw;} .prize-item:nth-of-type(3){top: .1vw;left: 54.6vw;} .prize-item:nth-of-type(4){top: 27.35vw;left: 54.6vw;} .prize-item:nth-of-type(5){top: 54.6vw;left: 54.6vw;} .prize-item:nth-of-type(6){top: 54.6vw;left: 27.35vw;} .prize-item:nth-of-type(7){top: 54.6vw;left: .1vw;} .prize-item:nth-of-type(8){top: 27.35vw;left: .1vw;} /*------------------禁止点击-------------*/ .refuse-click{filter: grayscale(10%);} </style> 复制代码
这篇关于用vuejs撸一个抽奖组件的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-04package.json 文件位置在哪?-icode9专业技术文章分享
- 2024-10-01Craco.js学习:从入门到实践指南
- 2024-10-01Create-React-App学习:入门与实践指南
- 2024-10-01CSS-in-JS学习:从入门到实践指南
- 2024-09-30JSX语法学习:从入门到初步掌握
- 2024-09-30Mock.js学习:入门教程与实战演练
- 2024-09-30React Hooks学习:从入门到实践
- 2024-09-30受控组件学习:React中的基础入门教程
- 2024-09-29JS定时器教程:初学者必看指南
- 2024-09-29JS对象教程:初学者的全面指南