看这个文章不错,借鉴 这个博主 的内容
样式是背景图片直接,没有设置。需要的话应该是
#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(
{/* 转盘 */}
{/* 指针 */}
)
}
}
这个就结束了。
在看看这个写的转盘活动抽奖。
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=0&&rdm<=randmArr[0]){
num = i+1
break
}
} else {
if(rdm>randmArr[i-1]&&rdm<=randmArr[i]){
num = i+1
break
}
}
}
控制旋转动画落点
虽然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,中奖概率还是比较大了,如果需要再小可以继续放小,
完整代码
转盘抽奖效果