使用vue简单实现一个转盘抽奖功能

这篇文章主要为大家详细介绍了vue简单实现转盘抽奖,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了vue简单实现转盘抽奖的具体代码,供大家参考,具体内容如下:

1.0 思路整理

转盘抽奖很常见,之前也没写过,现在有空来写写,分析如下:

1.1 转盘旋转 ----- 可以用 transform 的 rotate 来解决

1.2 旋转动画 ----- transition 过渡来处理

1.3 停留目标位置及中奖提示 ? ------ 通过控制旋转角度控制停留位置,中奖提示,考虑添加回调

1.1 开始行动

上面的思考,我们知道了大概要实现的步骤,首先我们搞张图片

这个圆盘有 10 份,每一份 360/10 = 36deg,假设要停留在第二个---->20金币,顺时针(含初始位置并计为1),即 共需要旋转 (2 - 1)* 36 = 36,这样,我们可以得出,停留位置需要旋转的角度 = (target - 1)*36。

1.2 中奖回调

上面的步骤,我们知道了如何控制到目标位置,那接下来就是事件通知,起初想的是,固定转动时间,然后开启定时器计时,很显然不靠谱,有没有什么可以在动画结束后就通知呢?transitionend,我找到了这个事件,可以监听元素动画结束事件,只不过有些兼容这个好办:

/**
动画结束事件兼容
**/
whichTransitionEvent(){
    let el = document.createElement('span'),
    transitions = {
        'transition':'transitionend',
        'OTransition':'oTransitionEnd',
        'MozTransition':'transitionend',
        'WebkitTransition':'webkitTransitionEnd'
    };
    for(let t in transitions){
        if( el.style[t] !== undefined ){
            return transitions[t];
        }
    }
    el = null;
}

2.0 完整示例

控制转动位置和事件通知都找到方法了,接下来开干!

栗子:

完整代码

<template>
    <div>
        <h3>转盘抽奖</h3>
        <div >
            <img ref=rotImg src=../assets/zhuan.png alt=>
            <div>
                <div ></div>
            </div>
        </div>
        <button @click=toDraw >点击抽奖</button>
    </div>
</template>
 
<script>
export default {
    name:'rotaryDraw',
    data() {
        return {
            rotate: 0,
            resetRotate: 0,
            hitId: 1,// 1-10
            drawStatus: false
        }
    },
    async mounted() {
        await this.$nextTick();
        let evenTransition = this.whichTransitionEvent();
        let img = this.$refs.rotImg;
        let that = this;
        const hitAre = [ '30M流量包','20金币','20M流量包','10M流量包','5金币',
            '谢谢参与','10金币','50M流量包','2金币','100M流量包'
        ];
        // 监听 动画结束 
        img.addEventListener(evenTransition,tranHand,false);
        function tranHand() {
            // 复位
            that.resetRotate = that.rotate > 360 ? that.rotate % 360 : 0;
            img.style.transition = none 0s ease 0s;
            img.style.transform = `rotate(${that.resetRotate}deg)`; 
            alert(`抽奖结果【 ${hitAre[that.hitId - 1]} 】`);
            that.drawStatus = false
        }
    },
    methods: {
        start() {
            this.$refs.rotImg.style.transition = all 3s ease 0s;
            this.$refs.rotImg.style.transform = `rotate(${this.rotate}deg)`;
        },
        toDraw() {
            if(this.drawStatus){
                console.log('正在抽奖中');
                return
            }
            // 标记状态
            this.drawStatus = true
            /**
             * 圆盘共 10 份 每份 36度, 停位置(id)度数 (id - 1)*36 
             * 基数 3圈 360*4
             * this.rotate 当前角度
             * **/ 
            let reset = 360 * 4;
            let idx = this.getRandomInt(1,11);
            // 设置命中
            this.hitId = idx;
            // 需要多转角度
            let addRotate = this.resetRotate > 0 ? 360 - this.resetRotate : 0;
            // 总共角度
            let allRotate = this.rotate + (idx - 1) * 36 + reset + addRotate;
            // 角度限制
            this.rotate = this.setRotate(allRotate);
            this.start()
        },
        // 递归计算角度 不超过 360*6
        setRotate(deg) {
            let rest = deg - 360;
            return rest > 360*6 ? this.setRotate(rest) : deg;
        },
        getRandomInt(min, max) {
            // 向上收
            min = Math.ceil(min);
            // 向下收
            max = Math.floor(max);
            return Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值
        },
        // 动画兼容
        whichTransitionEvent(){
            let el = document.createElement('span'),
            transitions = {
                'transition':'transitionend',
                'OTransition':'oTransitionEnd',
                'MozTransition':'transitionend',
                'WebkitTransition':'webkitTransitionEnd'
            };
            for(let t in transitions){
                if( el.style[t] !== undefined ){
                    return transitions[t];
                }
            }
            el = null;
        }
    }
}
</script>
 
<style >
.img_rotate{
    transform: rotate(0deg);
}
.round_box{
    width: 100%;
    max-width: 375px;
    position: relative;
    overflow: hidden;
}
img{
    width: 100%;
}
.center{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
}
.pointer{
    width: 5px;
    height: 90px;
    background-color: #f40;
    position: absolute;
    top: -90px;
}
.pointer::before{
    content:'';
    width: 0;
    height: 0;
    border-top: 15px solid transparent;
    border-right: 15px solid transparent;
    border-bottom: 15px solid #f40;
    border-left: 15px solid transparent;
    position: absolute;
    top: -20px;
    left: 50%;
    transform: translateX(-50%);
}
</style>

3.0 tips

总体来说有几个点需要注意

1、动画开始前上锁

2、动画结束后通知,状态复位

/**
比如:
基数3圈 reset 360*3
停留位置 第二个 (2 - 1)* 36 = 36
总共角度 360*3 + 36
动画停止后,因为还要继续旋转,所以不可能把角度一直增加,因此需要复位 
360*3 + 36 其实可以考虑 就转了 36度,然后再增加需要转的角度
**/

3、继续旋转,因为我们计算是以 30M流量 为初始值的,所以在此旋转 仍然需要以 30M为起点,此时假设 现在停留位置是 300度,也就是说 再转 60度,也就回到了初始位置,本人也是按照这个思路进行复位的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


https://segmentfault.com/a/1190000022018995 https://www.jianshu.com/p/8c3599dda094 vuex教程中,有这样一句话和这样一段代码: 实践中,我们会经常用到 ES2015 的参数解构来简化代码(特别是我们需要调用commi
ES6 (ECMAScript 6)中的模块是一个包含 JavaScript 代码的文件,在这个模块中所有的变量都对其他模块是不可见的,除非我们导出它。 ES6的模块系统大致分为导出(export)和导入(import)两个模块。 1、模块导出(export) 可以 导出 所有的最外层 函数 、 类
from https://mp.weixin.qq.com/s/-rc1lYYlsfx-wR4mQmIIQQ Vue知识点汇总(含Vue3) 一、Vue 基础 1. Vue的基本原理 当一个Vue实例创建时,Vue会遍历data中的属性,用 Object.defineProperty(vue3.0使
D:\Temp&gt;npm init vite@latest vue3study --template vuenpm ERR! code ETIMEDOUTnpm ERR! errno ETIMEDOUTnpm ERR! network request to https://registry.np
文章浏览阅读1.2k次。最近自己从零撸起的甘特图组件需要子组件的滚动条同步滚动这就涉及到子组件之间的互相通信,通过 消息总线可以达到我们的需求 ,首先建立一个标志位,拖动左边滚动条的时候,右边的滚动条事件不处理,反之拖动右边滚动条时,左边的滚动条事件不做处理,建立一个公共的变量用于两者的互斥store.jsimport Vue from 'vue'export let store = Vue.observable({ scrollFlag: true})export let mutations =.._vue 能不能同时有两个滚动事件
文章浏览阅读3.3k次,点赞3次,收藏16次。静默打印是什么?简单来说就是不需要用户点击"打印",自动去打印,但是使用浏览器web打印不可避免的要弹出以下画面面对这种问题也只能用"富客户端"技术来解决,在浏览器的沙盒安全模型中无法做到,那么只能使用插件的技术,这个我们就不自己花力气去做了,我找来了 lodop 这个免费的打印组件,功能还是挺强大的,下载下图的发行包解压后安装下图两个exe如果你的系统是64位的,可以安装install_lodop64.exe上图的LodopFuncs.js 是客户端要使用的核心库文件..._this.$getlodop().then((lodop) =>{
文章浏览阅读1.7k次。个人觉得大屏展示其实很简单,噱头多过技术含量,下面使用了 DataV (不是阿里的那个DataV哈,具体链接在这里)开发了一个大屏展示,使用了css flex弹性布局,使用了DataV的一些比较酷炫的边框(SVG写的),基本上功能没有全部完成,但是模子已经刻出来了,只是后端推送的内容没有全部写出来前端<template> <dv-full-screen-container class="screen-container"> <div class="ti_用signalr做一个简单的实时大屏显示
文章浏览阅读3.4k次,点赞3次,收藏10次。【说明】导入的Excel 字体颜色和背景色只能识别【标准色】,别的如"主题颜色",exceljs 解析出来不是颜色值。导入的样式包括字体,字号,列宽,合并单元格,【部分能识别】的背景色,文字颜色。导入到 x-data-spreadsheet 如下图。原Excel样式如下。_x-data-spreadsheet
文章浏览阅读1.7k次。之前参考某文章把 router-view 放在 el-tab-pane 外面都不起作用,问题根本不是出在 el-tab-pane,而是v-for 里面有多个route-view , keep-alive 时 tab 并未销毁掉,而是缓存隐藏了起来。需要把 router-view 的 name 与路由的 index.js 名称对应起来。之前参照很多文章修改试图修正这个问题,结果都徒劳,终于让我找到。我做了如下修改,主页面 main.vue。_el-tab-pane 后面接router-view
文章浏览阅读533次。今天在一台虚拟机上面运行老项目,报各种类型上图的错误提示,一开始还以为是less的问题,结果一个个装完还是报错,后面又说webpack, webpack cli有问题,头有点大了,google 一下,发现一个命令。讨论这个命令的文章,可以了解一下。运行以后终于出现了期待已久的。_npm install 忽略依赖
文章浏览阅读8k次,点赞3次,收藏12次。从这篇文章得到启发先定义一个组件从外部接收Template,然后在组件里调用<template > <div ref="markedContent"></div></template><script>import Vue from 'vue/dist/vue.esm.js'export default { name: 'wf-marked-content', props: ['content'], mounte.._vue components 动态传入模板
文章浏览阅读5.4k次。参考上一篇知识开发的一个功能,制作一个打印模板的管理模块,如下(就是保存froala编辑后的html文本,其中包括Vue的Template,这样我们可以利用Vue的模板的优势来动态绑定一些数据源进行HTML的打印,基本上跟过去水晶报表做一个模板再绑定数据源的方法异曲同工)在 main.js 里引用 froala 组件// Import and use Vue Froala lib.import VueFroala from 'vue-froala-wysiwyg'// 引入 Fr.._vue设计网页打印模板
文章浏览阅读992次。计划是这样,公司的项目一直在持续改动,安装包总是需要频繁生成新的,由此我想到了"持续集成"!有自动化工具不用,岂不可惜?这周的主要时间就用来学习CruiseControl.Net全面实现持续集成_怎么在vue的 script部分使用 eldigloa
文章浏览阅读1.2k次。其实Element UI 只用了文字提示的 el-tooltip 组件,不喜欢可以去掉,不记得是从哪拿到的原始代码,我给加了高亮渐变显示,图标,和拖拽时只能拖拽图标的位置,效果如上图,可以水平方向拖动,也可以垂直方向拖动。样式是less写的,css写嵌套样式太繁琐了。拿来主义,改造有理!下面贴代码<template> <div ref="splitPane" class="split-pane" :class="direction" :"{ fl..._element ui拉条样式
文章浏览阅读953次,点赞2次,收藏2次。接上一篇,这次加入的是从x-speadsheet导出Excel,并且带有x-speadsheet中的样式,重点关注 exportExcel 这个方法,我加入了 tinycolor 这个库用来翻译颜色值,值得注意的是, exceljs的颜色值是 argb 不是 rgba,一定不要弄混了a 是代表的透明度放在最前面_x-data-spreadsheet 导出
文章浏览阅读5.5k次,点赞2次,收藏21次。尝试了两个连线库 jsplumb 和 leadline ,其实两个库都很强大,但是基于个人使用的习惯,决定还是用 leadline ,在Vue 下我使用它的一个包装库 leader-line-vue 下面是上图的连接线示例代码,连接线很轻松的就实现了一个渐变效果..._vue 连线
文章浏览阅读4.2k次,点赞2次,收藏5次。首先官网推荐的安装方法没有生成dist文件,导致样式表等这些文件并没有生成npm install element-plus --save以上方法是有问题的,如果不幸执行了上面的命令,那么先执行卸载npm uninstall element-plus删除 main.js文件对element ui的引用,输入以下命令vue add element-plus..._elementui3.0
文章浏览阅读3.1k次。如上图,下面贴代码<template> <div> <el-date-picker size="large" style ="width:120px" v-model="selectYear" format="yyyy 年" value-format="yyyy" type="year" :clearable = "false" placeholder="选择年">.._vue多选周
文章浏览阅读1.8k次,点赞6次,收藏6次。经过 2021年的一个春节,从年前到现在,大致撸出一个 甘特图,进度条是用SVG画的,使用了几个工具库 (interactjs 用来处理拖拽和修改尺寸,snap.svg 用来处理 svg 的dom 操作,moment.js用来处理时间的操作),其他没有依赖任何的UI组件,目前初见雏形,还比较粗糙,后面会不断更新源码地址点击期间也摸索了怎么把vs code的项目上传到 GitHub 上面进行源代码的管理,基本上是参考的这篇文章做的..._vue gantt demo
文章浏览阅读2.1k次。接上两篇vue 下使用 exceljs + x-spreadsheet 带样式导入Excelvue 下使用 exceljs + x-spreadsheet 带样式导出Excel下面封装好一个组件调用组件的页面效果如图,目前“导出Json”还没有做_x-spreadsheet导入导出