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;}

自己在稍微改改啊
在这里插入图片描述
实现原理: 通过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 = 0const _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 = 7console.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+1break}} else {if(rdm>randmArr[i-1]&&rdm<=randmArr[i]){num = i+1break}}}
  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 = 0Object.keys(obj).map(item=>{num += obj[item]})console.log(num);}oPointer.onclick = function () {if (offOn) {offOn = !offOn;ratating();}}let n= 0var testTime = nullfunction test(){testTime = setInterval(()=>{n++if(n>99){clearInterval(testTime)}ratating();},300);}//旋转//TODO: 规定中奖区间,通过区间判定值var prevrdm = 0; //缓存上一次的旋转度var obj ={} //产生的随机数的次数var form = {} //统计中奖的概率var timer = nullfunction ratating() {var timer = null;const rdm =parseInt(Math.random() * (100 - 1) + 1);//随机的数var num = 7var 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+1break}} else {if(rdm>randmArr[i-1]&&rdm<=randmArr[i]){num = i+1break}}}console.log('num='+num);clearTimeout(timer);timer = nulltimer = setTimeout(function () {nextrdm = Math.floor((num* cat)-24); //定义本次抽奖结果var biginRotate = 2*360+(360-prevrdm) //定义默认的旋转圈数,同时补全使轮盘置零,prevrdm = nextrdmtransformRotate=nextrdm+biginRotate+transformRotate // 本次旋转的度// 测试中奖的概率时,把下面注释掉,同时将timeout时间设置为0oTurntable.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.mzph.cn/news/694169.shtml

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

相关文章

马斯克称首位受试者可凭思维操控鼠标;字节低调推出视频模型丨 RTE 开发者日报 Vol.148

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

微信小程序uniapp校园在线报修系统维修系统java+python+nodejs+php

管理员的主要功能有&#xff1a; 1.管理员输入账户登陆后台 2.个人中心&#xff1a;管理员修改密码和账户信息 3.用户管理&#xff1a;对注册的用户信息进行删除&#xff0c;查询&#xff0c;添加&#xff0c;修改 4.维修工管理&#xff1a;对维修工信息进行添加&#xff0c;修…

SpringBoot+MybatisPlus+Mysql实现批量插入万级数据多种方式与耗时对比

场景 若依前后端分离版本地搭建开发环境并运行项目的教程&#xff1a; 若依前后端分离版手把手教你本地搭建环境并运行项目_本地运行若依前后端分离-CSDN博客 若依前后端分离版如何集成的mybatis以及修改集成mybatisplus实现Mybatis增强&#xff1a; https://blog.csdn.net…

28/100二叉树的中序遍历 29/100不同的二叉搜索树(TODO) 30/100验证二叉搜索树 31/100对称二叉树

题目&#xff1a;28/100 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 题解&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() …

gitlab 项目上线,项目上线后回滚

gitlab 项目上线&#xff0c;项目上线后回滚 1.需要自己有个gitlab项目环境&#xff0c;没有找我&#xff0c;docker-compose 一键环境启动 2.发起合并请求3.选择合并的分支4.点击创建合并&#xff0c;然后确认合并合并完成&#xff0c;进行回滚操作&#xff0c;在合并详情页…

Vue | (三)使用Vue脚手架(下)| 尚硅谷Vue2.0+Vue3.0全套教程

文章目录 &#x1f4da;Vue 中的自定义事件&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;全局事件总线&#x1f407;使用方法&#x1f407;案例练习&#x1f407;TodoList案例优化 &#x1f4da;消息订阅与发布&#x1f407;使用方法…

压缩感知常用的测量矩阵

测量矩阵的基本概念 在压缩感知&#xff08;Compressed Sensing&#xff0c;CS&#xff09;理论中&#xff0c;测量矩阵&#xff08;也称为采样矩阵&#xff09;是实现信号压缩采样的关键工具。它是一个通常为非方阵的矩阵&#xff0c;用于将信号从高维空间映射到低维空间&…

10、内网安全-横向移动域控提权NetLogonADCSPACKDC永恒之蓝

用途&#xff1a;个人学习笔记&#xff0c;有所借鉴&#xff0c;欢迎指正&#xff01; 背景&#xff1a; 主要针对内网主机中的 域控提权漏洞&#xff0c;包含漏洞探针和漏洞复现利用。 1、横向移动-系统漏洞-CVE-2017-0146&#xff08;ms17-010&#xff0c;永恒之蓝&#xff0…

彩虹工具网程序开源未加密版源码,支持插件扩展

全新UI彩虹站长在线工具箱系统源码下载 全开源版本 支持暗黑模式 支持高达72种站长工具、开发工具、娱乐工具等功能。本地调用API、自带免费API接口&#xff0c; 是一个多功能性工具程序支持后台管理、上传插件、添加增减删功能 下载地址你别走吖 Σ(っ Д ;)っ (chaobiji.c…

Android13 编译ninja failed with: exit status 137

描述 现象很奇怪&#xff0c;主机是ubuntu 18.04&#xff0c; 内存有32G&#xff0c;并且系统中有两份Android13代码&#xff0c; 有一份编译正常&#xff0c;另外一份编译不正常&#xff0c;一度以为是因为下载源码不齐全导致&#xff0c;后面仔细看日志&#xff0c;原来是内…

Windows Server 2012 安装

1.镜像安装 镜像安装:Windows Server 2012 2.安装过程(直接以图的形式呈现) 2012激活秘钥:J7TJK-NQPGQ-Q7VRH-G3B93-2WCQD

WatiN——Web自动化测试(一)

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

Linux:gitlab创建组,创建用户,创建项目

创建组和项目 让后可以在组里创建一个个仓库 创建成员 我创建个成员再把他分配进这个组里 进入管理员 密码等会我们创建完用户再去配置密码 Regular是普通的用户&#xff0c;只可以正常去访问指定规则的项目 而下面的administrator就是管理员&#xff0c;可以随便进项目&…

基于springboot+vue的B2B平台的购物推荐网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

基于Web的网文管理系统

项目&#xff1a;基于Web的网文管理系统 伴随着时代的更替&#xff0c;我国人民的生活水平在不断提高&#xff0c;同时对生活娱乐的需求也在不断加大&#xff0c;我们在日常生活中通常会以多种方式娱乐自己&#xff0c;而阅读网络小说就是其中一种方式。随着计算机技术的成熟&a…

外包实在是太坑了,划水三年,感觉人都废了

先说一下自己的情况&#xff0c;专科生&#xff0c;19年通过校招进入杭州某个外包软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了3年的功…

数据结构第3章 串

名人说&#xff1a;莫道桑榆晚&#xff0c;为霞尚满天。——刘禹锡&#xff08;刘梦得&#xff0c;诗豪&#xff09; 本篇笔记整理&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 0、思维导图1、基本概念1&#xff09;主…

vue3 element-plus 省市区选择器组件封装

文章目录 前言一、省市区数据源处理二、AreaSelect省市区选择器组件封装三 、页面使用 前言 vue3封装一个省市区选择器组件&#xff0c;通过element-plus Cascader 级联选择器实现&#xff0c;效果如下&#xff1a; 一、省市区数据源处理 data.js var areaList {province_…

Unity NavMesh 清除不可行走区域

通常场景中物体设置为static或Navigation Static后&#xff0c;打开Navigation使用默认设置烘焙NavMesh&#xff0c;模型顶部和底部会出现蓝色网格&#xff0c;但其中有部分属于不可能到达区域&#xff0c;如下图 本文介绍两种可去掉NavMesh中不需要网格的方法&#xff1a; 方…

idm下载路径在哪 idm下载保存路径怎么设置 IDM下载器 internetdownloadmanager官方版下载 网络加速器

春节&#xff08;Spring Festival&#xff09;&#xff0c;是中国最隆重最富有特色的传统节日之一。春节期间我们与一家人团聚在一起&#xff0c;其乐融融。2024年春晚已经接近尾声了&#xff0c;很多人已经踏上了返程的路上。在部分地区&#xff0c;如春晚直播过程中或者网络高…