• react实现转盘抽奖功能


    看这个文章不错,借鉴 这个博主 的内容
    样式是背景图片直接,没有设置。需要的话应该是
    #bg {
    width: 650px;
    height: 600px;
    margin: 0 auto;
    background: url(turntable-bg.jpg) no-repeat;
    position: relative;
    }

        img[src^="pointer"] {
            position: absolute;
            z-index: 10;
            top: 155px;
            left: 247px;
        }
    
        img[src^="turntable"] {
            position: absolute;
            z-index: 5;
            top: 60px;
            left: 116px;
            transition: all 4s;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    自己在稍微改改啊
    在这里插入图片描述
    实现原理: 通过css3的 transition 和 transform 两个属性
    1 首先,我们简单定义一个奖品数组,实际开发中是调后台接口获取奖品,以下是为了方便演示

    const giftArr = [
    {giftName: 'iphone xs'},
    {giftName: '小米智能音箱'},
    {giftName: 'ThinkPad X390 LTE版'},
    {giftName: 'air pods 2'},
    {giftName: '雷蛇鼠标'}]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2、如下图划分区域,因为转盘旋转时是顺时针旋转,所以按照下图划分奖品区域,图中序号表示奖品数组每一项的index。
    在这里插入图片描述
    3、定义每个奖品的角度区域

    const LOTTERY_AREA_DEG = [[1, 59], [61, 119], [121, 179], [181, 239], [241, 299], [301, 359]]
    
    • 1

    这里我们把60度的整数倍度数给去掉了,是为了防止转到60度,120度这样的度数

    4、为了模拟抽中的奖品,我们写个方法随机生成奖品的序号,以及根据奖品序号拿到转盘需要转到的角度

    // 生成两个数范围内的随机整数
    const randomNum = (minNum, maxNum) => {
     return parseInt(Math.random() * (maxNum - minNum + 1) + 	minNum, 10);
    }
    
    const giftIndex = randomNum(0, 5)
    // 随机取对应奖品区域中的一个角度
    const targetDegree = randomNum(LOTTERY_AREA_DEG[giftIndex][0], 	LOTTERY_AREA_DEG[giftIndex][1])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5 逻辑思路:假设第一次抽中了奖品 “iphone xs” ,需要转到26°,第二次抽中了 “小米智能音箱”,需要转到82°,这种情况其实很好理解,我们只需用 transform: rotate(26deg) 和 transform: rotate(82deg);

    我们再来看另一种情况,假设第一次抽中了奖品 “air pods 2”,对应区域时3,需要转到221°,第二次抽中了 “小米智能音箱” ,对应区域是1,需要转到70°,那这种情况下,继续用 transform: rotate(70deg) 肯定是不行了,如果这样,会出现转盘逆时针转到区域1了,这样显然不是我们想要的结果,这种情况下我们就需要在70°的基础上再转360°,也就是转到430°的位置,才能达到顺时针旋转的效果。

    接着上面的情况,第三次,我们抽中了 “iphone xs”,对应区域是0,对应角度40°,我们按照上面的方法,40°比430°小,不能直接使用transform,那我们就给40°加360°,加了一个360°还不够430°,我们再加,加到2个360°后,发现360 * 2 + 40 = 760 > 430,可以,第三次转到730°就可以。

    从上面我们可以发现,下一次需要旋转到的角度一定要比上一次的度数要大, 注意:这里的角度是旋转到多少度,而不是旋转了多少度,这是两个不同的概念,笔者之前就是按照旋转了多少度来计算的,结果除了第一次能旋转到对应的区域,后面每一次都不会旋转到对应的区域了

    //  核心代码
    let rotateDeg = 0
    // 递归计算下次要转到的度数
    let i = 0
    const _fn = (n = 0) => {
        if (targetDegree + 360 * n > this.state.startRotateDeg) {
            rotateDeg = targetDegree + 360 * n
        } else {
            i++
            _fn(i)
        }
    }
    _fn()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    完整代码

    import React from 'react'
    export default class Lottery extends React.Component{
        constructor(props){
            super(props)
            this.state = {
                startRotateDeg: 0 // 记录上一次转到的角度
            }
        }
    
        randomNum = (minNum, maxNum) => {
            return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
        }
    
        handleClick = () => {
        	const LOTTERY_AREA_DEG = [[1, 59], [61, 119], [121, 179], [181, 239], [241, 299], [301, 359]]
            const giftIndex = this.randomNum(0, 5)
            // 随机取对应奖品区域中的一个角度
            const targetDegree = this.randomNum(LOTTERY_AREA_DEG[giftIndex][0], LOTTERY_AREA_DEG[giftIndex][1])
            let rotateDeg = 0
            // 递归计算下次要转到的度数
            let i = 0
            const _fn = (n = 0) => {
                if (targetDegree + 360 * n > this.state.startRotateDeg) {
                    rotateDeg = targetDegree + 360 * n
                } else {
                    i++
                    _fn(i)
                }
            }
            _fn()
            // 获取转盘实例
            const ele = document.getElementById('turntable')
            // 增加旋转动画
            ele.style.transition = 'all 6500ms'
            ele.style.transform = `rotate(${rotateDeg + 360 * 10}deg)` // 乘以10是为了转盘转动的效果
    
            this.setState({
                startRotateDeg: rotateDeg + 360 * 10   // 记录上一次旋转到的角度
            })
        }
    
        render(){
            return(
                
    {/* 转盘 */}
    {/* 指针 */}
    ) } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    这个就结束了。

    在看看这个写的转盘活动抽奖。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    1.实现旋转
    原理很简单,就是通过css动画来实现旋转动画,用js来控制旋转度,

        img[src^="turntable"] {
            position: absolute;
            z-index: 5;
            top: 60px;
            left: 116px;
            transition: all 4s;
        }
    //配合
    oTurntable.style.transform = "rotate(" +transformRotate+ "deg)";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.控制得奖概率
    控制概率,我将100当做概率,概率为 [randmArr[i-1],randmArr[i]),通过控制差值,来控制概率,当然这只是简单的demo,最好用map对象,更合理。

      var randmArr = [1,5,10,20,35,55,100] //概率计算为randmArr[i-1]和randmArr[i]之间
       const rdm =parseInt(Math.random() * (100 - 1) + 1);//随机的数
       var num = 7
       console.log('rdm='+rdm);
       // 统计随机数次数
       if (!obj[rdm]) {
             obj[rdm] = 1;
           } else {
             obj[rdm] ++;
           }
       for(var i = 0;i=0&&rdm<=randmArr[0]){
               num = i+1
               break
             }
         } else {
           if(rdm>randmArr[i-1]&&rdm<=randmArr[i]){
             num = i+1
               break
             }
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. 控制旋转动画落点
      虽然js是控制,单动画落点,也需要和我们所得一致,同时我们思考,一般抽奖都会固定旋转几圈,所以我们需要给个初始的旋转圈数,同时下一次旋转又是从上一次落点的基础上进行的,为了控制我们每次都是从原点开始,这样才能控制好落点和js一致,于是我设置初始圈为3圈,而第三圈为,第一次旋转后剩下度数(360-n),这样就能达到每次都是从原点开始。

       	nextrdm = Math.floor((num* cat)-24); //定义本次抽奖结果
          var biginRotate = 2*360+(360-prevrdm) //定义默认的旋转圈数,同时补全使轮盘置零,
          prevrdm = nextrdm //缓存本次次的角度
             transformRotate=nextrdm+biginRotate+transformRotate //本次旋转的度
          oTurntable.style.transform = "rotate(" +transformRotate+ "deg)";
      
      • 1
      • 2
      • 3
      • 4
      • 5

    4.测试概率
    写是写完了,但不测试都是瞎扯淡,所以我定义了2个对象,用了记录每次所得的随机数和次数,还有中几等奖的次数
    obj 和 form对象

    const rdm =parseInt(Math.random() * (100 - 1) + 1);//随机的数 
    // 统计随机数次数 
    if (!obj[rdm]) {
        obj[rdm] = 1;
    } else {
        obj[rdm] ++;
    }
    
      // 统计中奖概率
    if ( !form['a'+num]) {
        form['a'+num] = 1;
    } else {
        form['a'+num]++
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述
    打印发现其实随机比较公平的,但是毕竟用的100,中奖概率还是比较大了,如果需要再小可以继续放小,

    完整代码

    
    
    
        
        
        
        转盘抽奖效果
        
        
    
    
        
        
    pointer turntable
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
  • 相关阅读:
    Three.js 实现3D开放世界小游戏:阿狸的多元宇宙 🦊
    shell脚本之ftp命令
    【日拱一卒行而不辍20220921】自制操作系统
    vue——计算属性、侦听属性、组件、组件通信、ref属性、数据总线、动态组件、插槽
    红黑树树插入后自平衡
    机器学习策略篇:详解如何使用来自不同分布的数据,进行训练和测试(Training and testing on different distributions)
    【c++ primer 笔记】第 16章 模板与泛型编程
    Apache简介与安装
    学习 MySQL 需要知道的 28 个小技巧
    202.快乐数
  • 原文地址:https://blog.csdn.net/lzfengquan/article/details/136192696