转盘的简单实现
通过代码实现抽奖的转盘,使用html、css、js实现(并未使用canvas)。参考例子:JS写的一个抽奖小Demo从普通写法到设计模式再向ES6的进阶路程。
实现
样式实现
ul是转盘部分,将转盘划分成12份。pointer则是点击转动部分。主要的html结构如下:
<ul class="turntable"> <li><span>1</span></li> <li style="transform: rotateZ(30deg);"><span>2</span></li> <li style="transform: rotateZ(60deg);"><span>3</span></li> <li style="transform: rotateZ(90deg);"><span>4</span></li> <li style="transform: rotateZ(120deg);"><span>5</span></li> <li style="transform: rotateZ(150deg);"><span>6</span></li> <li style="transform: rotateZ(180deg);"><span>7</span></li> <li style="transform: rotateZ(210deg);"><span>8</span></li> <li style="transform: rotateZ(240deg);"><span>9</span></li> <li style="transform: rotateZ(270deg);"><span>10</span></li> <li style="transform: rotateZ(300deg);"><span>11</span></li> <li style="transform: rotateZ(330deg);"><span>12</span></li> </ul> <div class="pointer" > <p onclick="startRotate()">开始抽奖</p> </div>实现的效果如图:
![]()
ul相对定位后,li设为绝对定位。li样式使用border做成三角形,父元素设置overflow: hidden。然后再对每个li进行rotate变换就可以实现如图的形状。再将开始按钮放置到中间盖住中心部分,就实现如图的整体效果。
转盘转动实现
其实在点击的时候,就已经确定好指针要指向的部分。转盘分为12份,则随机一个1-12的数,每一部分在圆中所占据的角度是30°。例如:第一部分(1)占据了345-15°,则12占据了16-45°…以此类推。
因为要制作一个转动的过程,所以需要再随机一个数,这个数就代表要转的圈数,圈数乘上360,加上选中的数所在的度数,即
转盘转动的总度数 = 圈数 * 360° + 度数
但由于每一部分占据的是一个范围,所以指针最后所在的区间范围(以12为例):
max度数 = 圈数 360° + 45°
min度数 = 圈数 360° + 16°
这里肯可能有疑问:12在圆中为什么是16-45°,而不是316-345°。原因是:转盘是按顺时针方向进行转动的,所以当转盘转动时,1过后指针指向的是12、11、10、9…。所以在计算对应位置时,是按1、12、11…的顺序进行的。同时由于1占据345-15°这样一个范围,为了方便计算,我们将其范围设为-16-15°。同时,为了避免指针在转动结束时指向两个部分的交界处,显示不明显,所以我们在左右各设置一个缓冲区,让指针最后不会停留在缓冲区。如图:
为了转盘在开始转动和结束时带有速度递增与递减的效果,需要设置一个加速度、减速度,还要计算出在减速过程中所需要转过的度数,得到开始减速的度数
min度数 - 减速过程中转过的度数 = 开始减速的度数
数据预处理的逻辑如下:
>
function prepareData(number){
// 默认12格
number ? ‘’ : number = 12;
let everyDeg = 360 / number,
bufferDeg = 8, // 左右各缓冲4deg
numbersDeg = [];
// 每格所占的度数
let initCount = (everyDeg-bufferDeg)/2;
numbersDeg[1] = [-initCount , initCount];
for(let i=number ; i >= 2 ; i--){
numbersDeg[i+''] = [initCount+bufferDeg+1 , initCount+everyDeg];
initCount += everyDeg;
}
let randomValue = Math.floor(Math.random()*number)+1, // 随机被选中的数
rotateCount = Math.floor(Math.random()*3)+6, // 旋转6-8圈
maxStep = Math.floor(Math.random()*3)+6, // 最大转速为6-8(deg)
stepPlus = 0.05, // 转动的加速度
stepLess = 0.02, // 转动的减速度
step = 0, // 初始变化的角度
initDeg = 0; // 初始转过角度
let totalDeg = rotateCount * 360,
maxDeg = totalDeg + numbersDeg[randomValue][1],
minDeg = totalDeg + numbersDeg[randomValue][0],
// 每次变化的角度在衰减过程中是等差数列,则在衰减过程中会转过的度数之和
// 公式 ((startStep + maxStep) * n) / 2
restDeg = (maxStep * (maxStep / stepLess)) /2;
return {
result: randomValue,
maxDeg: maxDeg,
minDeg: minDeg,
maxStep: maxStep,
step: 0,
initDeg: 0,
stepPlus: stepPlus,
stepLess: stepLess,
restDeg: restDeg,
rotateState: 'stop' // 当前转盘状态['plus','less','stop']
};
}
开始抽奖,通过js代码改变转盘的rotate度数,就可以实现转盘的转动。不过还要考虑加速、减速、停止。
function rotate(){ turntable.style.transform = "rotateZ("+data.initDeg+"deg)"; // 计算下一次转盘的度数 data.initDeg += data.step;
// 加速
if(data.step < data.maxStep && data.rotateState == 'plus'){
data.step += data.stepPlus;
}
// 减速
else if(data.initDeg >= data.minDeg - data.restDeg){
data.step -= data.stepLess;
data.rotateState = 'less';
}
if(data.initDeg >= data.minDeg && data.initDeg <= data.maxDeg && data.step < 0){
alert(data.result);
resetTurntable();
console.log(`step: ${data.step}, deg: ${data.initDeg}`);
}else{
window.requestAnimationFrame(rotate);
}
}
以上便是一个转盘实现的大致逻辑与代码。最后转动的效果如图: