VueCSS3实现转盘抽奖

最近有个转盘抽奖的需求,搜了一下现有的轮子,有的是用jQuery的动画函数实现的,有的是用canvas绘图然后再用高频率的setTimeout调用旋转方法,前者太老了没法简单移植到vue项目,后者感觉性能表现可能不会太好。也有一些用CSS动画的方案,设计了加速-匀速-减速三个动画,再计算偏转角度让三个动画尽可能无缝衔接,但我感觉绕了大远路,应该有更简单轻量的实现方案。个人更倾向于用transition来实现,不过网上的例子感觉还不够好,有的倾斜文字都没有对齐,最后还是自己手写了一个。无需jQuery,不用js脚本实现动画细节,动画效果完全连续无需衔接,支持动态设置2个以上任意数量的奖项。核心思路是用transition以及rotate实现旋转动画,使用transition-origin和rotate绘制出定位较为精确的轮盘奖项。 代码及实例展示 <script src="//unpkg.com/vue/dist/vue.js"></script><span id="app"> <span>Prize number: {{ prizeNumber }}</span> <button type="button" @click="!rolling && prizeNumber < 8 && (prizeNumber++)" :disabled="rolling || prizeNumber === 8">Add</button> <button type="button" @click="!rolling && prizeNumber > 2 && (prizeNumber--)" :disabled="rolling || prizeNumber === 2">Remove</button> <div class="wheel-wrapper"> <div class="wheel-pointer" @click="onClickRotate" > Start </div> <div class="wheel-bg" :class="{freeze: freeze}" :style="`transform: rotate(${wheelDeg}deg)`" > <div class="prize-list"> <div class="prize-item-wrapper" v-for="(item,index) in prizeList" :key="index" > <div class="prize-item" :style="`transform: rotate(${(360/ prizeList.length) * index}deg)`" > <div class="prize-name"> {{ item.name }} </div> <div class="prize-icon"> <img :src="item.icon"> </div> </div> </div> </div> </div> </div></div>html { background: #DD7C7D;}.wheel-wrapper { width: 300px; height: 300px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .wheel-pointer { width: 60px; height: 60px; border-radius: 1000px; background: yellow; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); text-align: center; line-height: 60px; z-index: 10; } .wheel-bg { width: 100%; height: 100%; border-radius: 1000px; overflow: hidden; transition: transform 4s ease-in-out; background: #7EEF97; &.freeze { transition: none; background: red; } } .prize-list { width: 100%; height: 100%; position: relative; text-align: center; } .prize-item-wrapper { position: absolute; top: 0; left: 50%; transform: translateX(-50%); width: 150px; height: 150px; } .prize-item { width: 100%; height: 100%; transform-origin: bottom; .prize-name { padding: 16px 0; } .prize-icon {} }var Main = { data() { return { freeze: false, rolling: false, wheelDeg: 0, prizeNumber: 8, prizeListOrigin: [ { icon: "https://picsum.photos/40?random=1", name: "$10000" }, { icon: "https://picsum.photos/40?random=6", name: "Thank you!" }, { icon: "https://picsum.photos/40?random=2", name: "$500" }, { icon: "https://picsum.photos/40?random=3", name: "$100" }, { icon: "https://picsum.photos/40?random=6", name: "Thank you!" }, { icon: "https://picsum.photos/40?random=4", name: "$50" }, { icon: "https://picsum.photos/40?random=5", name: "$10" }, { icon: "https://picsum.photos/40?random=6", name: "Thank you!" } ] }; }, computed: { prizeList () { return this.prizeListOrigin.slice(0, this.prizeNumber) } }, methods: { onClickRotate() { if (this.rolling) { return; } this.rolling = true; const { wheelDeg, prizeList } = this; const random = Math.floor(Math.random() * (prizeList.length )); console.log(random); this.wheelDeg = wheelDeg - wheelDeg % 360 + 6 * 360 + (360 - 360 / prizeList.length * random); setTimeout(() => { this.rolling = false; alert("Result:" + prizeList[random].name); }, 4500); } }, watch: { prizeNumber () { this.freeze = true this.wheelDeg = 0 setTimeout(() => { this.freeze = false }, 0) } }};var App = Vue.extend(Main);new App().$mount("#app");

June 25, 2019 · 2 min · jiezi

半小时撸一个抽奖程序

需求总是很紧急,昨天正在开会收到人力需求,有时间做个抽奖吗?(now 下午四点12,年会五点开始。)还没能等我拒绝,人事又补了一句做不出来我们就不抽奖了,我擦瞬间感觉要是搞不出来会被兄弟们捅死的节奏,默默的删除了没时间做的消息,重新写了四个字名单给我。还好去年前年都是我搞得很庆幸没被当场打脸,重启去年程序(需要收集全员头像并ps)时间显然不够,庆幸的是还有点经验,会议结束时间已经四点半了。好了不扯淡了开始干活吧!先屡一下思路1、好看是好看不了了,别指望没设计没时间程序员搞出来的效果。2、样式开始按钮、停止按钮、人员名单别列表、抽中名单列表。3、点击开始,首先乱序名单列表保证每次抽奖列表顺序不一样,防止他们怀疑我作弊搞权重(就TM半小时哪有时间搞权重)时间紧任务重,直接用的lodash shuffle方法来乱序视图4、随机数是肯定要有的,每隔200ms随机一个从0到人员个数(数组长度随机整数)5、抽中人员和没抽中人员分两个数组存入localStorage,防止抽奖过程中刷新页面,纯静态不存本地那场面就尴尬了每次刷新完如果本次存储了从本地获取人员列表和中奖名单6、点击end选中当前随机数在页面上高亮显示。接下来看整体实现代码//依赖js<script src=“https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script><script src=“https://lib.baomitu.com/lodash.js/4.14.1/lodash.min.js"></script><body> <div id=“list-complete-demo” class=“demo”> <button v-on:click=“start”>start</button> <button v-on:click=“end”>end</button> <div class=“draw-list”> <span v-for=“item in target”>{{item}}</span> </div> <transition-group name=“list-complete” tag=“p”> <span v-for=“item in arrList” v-bind:key=“item” class=“list-complete-item” :class=”{‘draw-bg’: item == value}"> {{ item }} </span> </transition-group> </div> <script> new Vue({ el: ‘#list-complete-demo’, data: { arrList: [ “张三”, “李四”, “王五”, “赵六”, “陈七”, “张扒”, “李十四”, “王十五”, “赵十六”, “陈十七”, “张二三”, “李二四”, “王二五”, “赵二六”, “陈二七”, “张二扒”, “李三四”, “王三五”, “赵三六”, “陈三七” ], target: [],//中奖名单 index: -1,//当前随机索引 timer: null,//定义一个定时器 value: ‘’,//当前人员名 status: true//当前抽奖状态 }, mounted() { if (!localStorage.getItem(‘initData’)) { localStorage.setItem(‘initData’, JSON.stringify(this.arrList)) } else { this.arrList = JSON.parse(localStorage.getItem(‘initData’)) } if (localStorage.getItem(‘drawList’)) { this.target = JSON.parse(localStorage.getItem(‘drawList’)) } }, methods: { start() { if (this.status) { if (this.index != -1) { this.arrList.splice(this.index, 1) localStorage.setItem(‘initData’, JSON.stringify(this.arrList)) } this.shuffle() setTimeout(() => { this.recursive() }, 800) this.status = !this.status } }, randomIndex: function() { this.index = Math.floor(Math.random() * this.arrList.length) return this.index }, remove: function() { this.arrList.splice(this.randomIndex(), 1) }, shuffle: function() { this.arrList = _.shuffle(this.arrList) }, recursive() { clearInterval(this.timer) this.timer = setTimeout(() => { this.value = this.arrList[this.randomIndex()] this.recursive() }, 200) }, end() { clearInterval(this.timer) this.index = this.randomIndex() this.value = this.arrList[this.index] this.target.push(this.value) localStorage.setItem(‘drawList’, JSON.stringify(this.target)) this.status = !this.status } } }) </script></body>体验下效果需求搞定,经现场测试目前没发现什么问题!如有疑问随时回复留言! ...

January 30, 2019 · 1 min · jiezi