react实现转盘抽奖功能

news2025/1/12 6:03:11

看这个文章不错,借鉴 这个博主 的内容
样式是背景图片直接,没有设置。需要的话应该是
#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;
    }

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

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

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

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

这里我们把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])

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()

完整代码

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(
            <div>
                {/* 转盘 */}
                <div className="turntable" id="turntable"></div>
                {/* 指针 */}
                <div className="pointer" onClick={this.handleClick}></div>
            </div>
        )
    }
}

这个就结束了。

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

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

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<randmArr.length;i++){
     if(i===0){
         if(rdm>=0&&rdm<=randmArr[0]){
           num = i+1
           break
         }
     } else {
       if(rdm>randmArr[i-1]&&rdm<=randmArr[i]){
         num = i+1
           break
         }
     }
 }
  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)";
    

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]++
}

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

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="description" content="">
    <meta name="author" content="marendu">
    <title>转盘抽奖效果</title>
    <!-- 这里是css部分 -->
    <style>
        #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;
        }
    </style>
</head>
<body>
    <!-- 这里是HTML结构部分 -->
    <div id="bg">
    	<img src="pointer.png" alt="pointer">
    	<img src="turntable.png" alt="turntable">
    </div>
    <button onclick="btn()">概率统计</button>
    <button onclick="test()">测试100次抽奖</button>
    <!-- 这里是js部分 -->
    <script>
        var oPointer = document.getElementsByTagName("img")[0];
        var oTurntable = document.getElementsByTagName("img")[1];
        var cat = 51.4; //总共7个扇形区域,每个区域约51.4度
        var offOn = true; //是否正在抽奖
        var transformRotate = 0 //转圈度数
        var randmArr = [1,5,10,20,35,55,100] //概率计算为randmArr[i-1]和randmArr[i]之间
        // 打印概率
        function btn(){
            console.log(form);
            console.log(obj);
            let num = 0
          Object.keys(obj).map(item=>{
            num += obj[item]
          })
          console.log(num);
        }
        oPointer.onclick = function () {
            if (offOn) {
                offOn = !offOn;
                ratating();
            }
        }
          let n= 0
          var testTime = null
          function test(){
            testTime = setInterval(()=>{
                n++
                if(n>99){
                clearInterval(testTime)
             }
               ratating();
           },300);
      }
        //旋转
        //TODO: 规定中奖区间,通过区间判定值
        var prevrdm = 0; //缓存上一次的旋转度
        var obj ={} //产生的随机数的次数
        var form = {} //统计中奖的概率
        var timer = null
        function ratating() {
            var timer = null;
            const rdm =parseInt(Math.random() * (100 - 1) + 1);//随机的数
            var num = 7
            var nextrdm = 0; //本次旋转度
            console.log('rdm='+rdm);
            // 统计随机数次数
            if (!obj[rdm]) {
                  obj[rdm] = 1;
                } else {
                  obj[rdm] ++;
                }
            for(var i = 0;i<randmArr.length;i++){
              if(i===0){
                  if(rdm>=0&&rdm<=randmArr[0]){
                    num = i+1
                    break
                  }
              } else {
                if(rdm>randmArr[i-1]&&rdm<=randmArr[i]){
                  num = i+1
                    break
                  }
              }
          }
          console.log('num='+num);
            clearTimeout(timer);
            timer = null
            timer = setTimeout(function () {
              nextrdm = Math.floor((num* cat)-24); //定义本次抽奖结果
              var biginRotate = 2*360+(360-prevrdm) //定义默认的旋转圈数,同时补全使轮盘置零,
              prevrdm = nextrdm
              transformRotate=nextrdm+biginRotate+transformRotate // 本次旋转的度
              // 测试中奖的概率时,把下面注释掉,同时将timeout时间设置为0
              oTurntable.style.transform = "rotate(" +transformRotate+ "deg)";
                    setTimeout(function () {
                        offOn = !offOn;
                        // 统计中奖概率
                          if ( !form['a'+num]) {
                            form['a'+num] = 1;
                          } else {
                            form['a'+num]++
                          }
                        console.log('transformRotate='+transformRotate);
                        if (nextrdm <= cat * 1) { 
                        	console.warn("一等奖"); 
                        	console.log("nextrdm=" + nextrdm + "rdm=" + rdm + "," + "4999元");
                        }
                        else if (nextrdm <= cat * 2) {
	                        console.warn("二等奖"); 
	                        console.log("nextrdm=" + nextrdm + "rdm=" + rdm + "," + "50元");
	                    }
                        else if (nextrdm <= cat * 3) {
                            console.warn("三等奖");
                            console.log("nextrdm=" + nextrdm + "rdm=" + rdm + "," + "10元");
                        }
                        else if (nextrdm <= cat * 4) {
                            console.warn("四等奖");
                            console.log("nextrdm=" + nextrdm + "rdm=" + rdm + "," + "5元");
                        }
                        else if (nextrdm <= cat * 5) {
                            console.warn("五等奖");
                            console.log("nextrdm=" + nextrdm + "rdm=" + rdm + "," + "免息服务");
                        }
                        else if (nextrdm <= cat * 6) {
                            console.warn("六等奖");
                            console.log("nextrdm=" + nextrdm + "rdm=" + rdm + "," + "提交白金");
                        }
                        else if (nextrdm <= cat * 7) { 
                             console.warn("七等奖");
                             console.log("nextrdm=" + nextrdm + "rdm=" + rdm + "," + "未中奖");
                       }
                  }, 0);
            }, 30);
        }
    </script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1459608.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

redis的搭建 RabbitMq搭建

官网 Download | Redis wget https://github.com/redis/redis/archive/7.2.4.tar.gz 编译安装 yum install gcc g tar -zxvf redis-7.2.4.tar.gz -C /usr/localcd /usr/local/redis make && make install 常见报错 zmalloc.h:50:10: fatal error: jemalloc/jemal…

[office] excel图表怎么发挥IF函数的威力 #微信#媒体

excel图表怎么发挥IF函数的威力 IF函数应该是最常用的Excel函数之一了&#xff0c;在公式中经常能够看到她的“身影”。IF函数的基本使用如图1所示。 图1 IF函数之美 IF函数是一个逻辑函数&#xff0c;通过判断提供相应操作&#xff0c;让Excel更具智能。 然而&#xff0c;…

js设计模式:装饰者模式

作用: 可以给原有对象的身上添加新的属性方法 可以让对象或者组件进行扩展 示例: class Person{constructor(name,selfSkill){this.name namethis.selfSkill selfSkill}run 会走路}//所有人类都有的共同特性和技能let wjt new Person(王惊涛,写代码)let mashi new Pers…

2024.02.20作业

1. 使用多进程完成两个文件的拷贝&#xff0c;父进程拷贝前一半&#xff0c;子进程拷贝后一半&#xff0c;父进程回收子进程的资源 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <time.h> #includ…

Sora的原理,中国小学生游戏在践行

大家龙年好呀&#xff0c;春节假期和家人出去浪了&#xff0c;旅行期间&#xff0c;几乎没刷社交媒体信息。等我17号回到家仔细看手机&#xff0c;Sora的消息铺面而来&#xff0c;什么“新革命”、“划时代”、“新纪元”说的挺神呼。 任何新事物出现&#xff0c;讨论热烈是好…

AS-V1000 视频监控平台产品介绍:客户端功能介绍(四)

目 录 一、引言 1.1 AS-V1000视频监控平台介绍 1.2平台服务器配置说明 二、软件概述 2.1 客户端软件用途 2.2 客户端功能 三、客户端功能说明 3.1告警管理 3.1.1告警联动 &#xff08;1&#xff09;告警联动显示 &#xff08;2&#xff09;告警联动处理 3…

unity学习(31)——跳转到角色选择界面(打勾?手滑挂错脚本)

There are 2 audio listeners in the scene. Please ensure there is always exactly one audio listener in the scene. 是因为后来创建了一个camera&#xff0c;因为camera中自带一个组件Audio Listener。所以有两个camera就有两个audio listener导致报错。 一个简单的解决…

C++(18)——适配器概念以及stack、queue、优先队列的模拟实现

上篇文章中&#xff0c;给出了对于模拟实现中功能的补全&#xff0c;本篇文章将优先介绍一个新的容器之后引入什么是适配器&#xff0c;以及适配器的使用方法&#xff0c;再通过适配器的思想来完成对于&#xff0c;、优先级队列_的实现。 目录 1. deque: 1.1 什么是deque&…

【嵌入式-Keil】keil代码提示快捷键

CTRL空格 如果没有提示&#xff0c;可能跟输入法的快捷键冲突&#xff0c; 右键->设置->按键->勾掉第一个就行了 再按CTRL空格就有提示了 参考&#xff1a;串口发送&串口发送接收

SAP PP学习笔记02 - PP中配置品目Master时的顺序

配置品目Master的时候&#xff0c;最佳实践是要遵循什么顺序呢&#xff1f; 一般而言是如下顺序 - 新规物料类型&#xff08;或利用现有类型也可以&#xff09; - 设定料号范围 - 设定物料状态&#xff08;比如准备好之前&#xff0c;要先锁住&#xff0c;等准备好了之后再…

CTFshow web(SQL注入176-179)

web176 没啥好说的&#xff0c;直接上万能密码&#xff1a; 1 or usernameflag 当然了还有别的方法&#xff1a; 1 union Select 1,2,group_concat(password) from ctfshow_user where username flag -- web177 没啥好说的&#xff0c;直接上万能密码&#xff1a; 1 or user…

《VitePress 简易速速上手小册》第1章:VitePress 入门(2024 最新版)

文章目录 1.1 VitePress 简介与架构1.1.1 基础知识点解析1.1.2 重点案例&#xff1a;企业文档站点1.1.3 拓展案例 1&#xff1a;个人博客1.1.4 拓展案例 2&#xff1a;产品展示网站 1.2 安装与初次运行1.2.1 基础知识点解析1.2.2 重点案例&#xff1a;公司内部知识分享平台1.2.…

关于VIT(Vision Transformer)的架构记录

在VIT模型设计中&#xff0c;尽可能地紧密遵循原始的Transformer模型&#xff08;Vaswani等人&#xff0c;2017年&#xff09;。这种刻意简化的设置的一个优势是&#xff0c;可扩展的NLP Transformer架构及其高效的实现几乎可以即插即用。 图&#xff1a;模型概述。我们将图像分…

MySQL在OpenEuler中的安装及数据库的备份

MySQL在OpenEuler中的安装 MySQL以二进制形式进行安装 1.获取软件包 &#xff08;在进行获取时&#xff0c;检查网络是否通畅&#xff09; wget -c https://mirrors.aliyun.com/mysql/MySQL-8.0/mysql-8.0.28-linux-glibc2.12-x86_64.tar.xz2.创建用户组和用户 groupadd -g…

图片怎么变成透明背景?分享这些变透明的方法

很多从事编辑和图片设计的同行在日常工作中经常需要处理图片的背景色。为了更好地进行设计和编辑&#xff0c;将图片的背景色替换成透明是非常必要的。然而&#xff0c;对于一些新手来说&#xff0c;使用专业的图像处理软件可能有些困难。不过&#xff0c;现在有很多在线的图像…

大厂的数据质量中心系统设计

日常工作中&#xff0c;数据开发上线完一个任务后并不是就可以高枕无忧&#xff0c;时常因上游链路数据异常或者自身处理逻辑的 BUG 导致产出的数据结果不可信。而问题发现可经历较长周期&#xff08;尤其离线场景&#xff09;&#xff0c;往往是业务方通过上层数据报表发现数据…

网页布局之浮动

一&#xff0c;传统网页布局的三种方式 普通流&#xff08;标准流&#xff09;、浮动、定位。 二&#xff0c;标准流&#xff08;普通流/文档流&#xff09; 即为标签按照规定好的默认方式排列。 1.块级元素会独占一行&#xff0c;从上向下顺序排列。 常用元素&#xff1a;…

多进程-day3

1、使用多进程完成两个文件的拷贝&#xff0c;父进程拷贝前一半&#xff0c;子进程拷贝后一半&#xff0c;父进程回收子进程的资源 #include <myhead.h> int main(int argc, const char *argv[]) {FILE *fp1NULL;FILE *fp2NULL;if((fp1fopen("./simple.txt",&…

如何使用CloakQuest3r获取受安全服务保护的网站真实IP地址

关于CloakQuest3r CloakQuest3r是一款功能强大的纯Python工具&#xff0c;该工具可以帮助广大研究人员获取和查看受Cloudflare和其他安全服务商保护的网站真实IP地址。 Cloudflare是一种广泛采用的网络安全和性能增强服务&#xff0c;而CloakQuest3r的核心任务就是准确识别隐…

Uibot (RPA设计软件)智能识别信息+微信群发助手(升级版)———课后练习2

解决痛点&#xff1a; Excel如何计算两个日期之间相差月数 方法&#xff1a; 1、首先打开要进行操作的Excel表格。 2、打开后选中要计算相差月数的单元格。 3、然后输入公式&#xff1a;DATEDIF(A2,B2,"m")&#xff0c;输入完成后点击回车键。 4、在弹出的窗口中&a…