最近公司要做一個轉盤抽獎的效果,但是我們可以控制轉盤抽獎的概率,自己用es6簡單的實現了下,中間雖然遇到一些問題,但最終都是解決了,下面說一下我的思路。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/index.css">
<script src="js/index.js"></script>
</head>
<body>
<div class="main">
<div class="active">
<div class="rotate_title"><img src="images/rotate_title.png" alt=""></div>
<div class="rotate">
<div class="zd_round"><img src="images/zd.png" alt=""></div>
<div class="pointer"><img src="images/pointer.png" alt="" width="100%"></div>
</div>
</div>
</div>
<script>
new Draw({
el: ".zd_round img",//轉動的元素
btn:".pointer", //點擊轉動的元素
count: 3, //轉盤轉動的圈數,
chance: {
1: "40%",
5: "40%", //指定抽獎概率,其余則平分
},
num:8,//轉盤的總個數
innerTime:4000, //轉盤轉動的時間
endFunc:function(pos){
console.log(pos)
} //轉盤動畫結束后的回調函數
})
</script>
</body>
</html>
上面是html代碼,我就不多做介紹了,效果原理主要還是利用css3 的rotate屬性實現元素的旋轉,下面我們看js代碼,我們使用的事面向對象編程,我們可以在前台配置參數多次調用 。下面我們開始js代碼的編寫:
(function () {
function $_(name) {
return document.querySelector(name);
}
class Draw {
constructor(opt) {
}
}
window.Draw=Draw;
})();
首先我們通過一個匿名函數自執行的方式,這樣做的好處的是防止內部的變量污染全局,里面的變量都形成了一個局部變量,最下面我們通過把Draw類暴露給全局,這樣我們就可以在外部直接調用。
constructor(opt) {
let defaultopt = {
el: ".zd_round img",//轉動的元素
btn: ".pointer", //點擊轉動的元素
count: 3, //轉盤轉動的圈數,
chance: {
1: "40%",
5: "40%", //指定抽獎概率,其余則平分
},
num: 8,//轉盤的總個數
innerTime: 4000 //轉盤轉動的時間
}
opt = Object.assign({}, defaultopt, opt);
this.init(opt);
}
我們需要知道的是這是一個構造函數,當我們new 一個類的時候會執行構造函數,把this綁定的屬性方法添加到這個類上面,可以看到我們做的事把我們外部調用的參數和我們內部調用的參數的一個合並,我們用到的是es6的Object.assign的方法,不同的童鞋請自行百度,最后我們得到了合並后的一個對象。
init(opt) {
let { el, chance, count, num, btn, innerTime ,endFunc} = opt;
let numArray = Object.keys(chance);
this.residue = [];
for (let i = 0; i < num; i++) {
if (!numArray.includes(String(i))) {
this.residue.push(i)
}
}
this.pos = this.getNum(chance); //確定轉盤最后停留的位置
this.deg = 360 / num;
this.rotate(el, count, btn, innerTime,endFunc);
}
init函數主要做一些初始化操作,通過es6解構賦值保存傳進來的參數,主要難點怎么控制概率,我下面定義了一個方法getNum來獲取最后停留的位置,我定義了一個變量得到0-100的隨機數,通過傳進來的概率值來判斷,這個不好描述,通過舉例說明:
var n1 = Math.round(Math.random() * 100); //獲取100之內的任意一個整數;
if (n1 < 95) {
console.log(我是幾率為95 % 的)
} else {
console.log(我是幾率為5 % 的)
}
有了這個思路就簡單多了,我們可以通過傳進來的概率對象進行遍歷來判斷,如果在概率范圍內直接return出這個對象的索引,也就是概率的選項,那么就會有一個問題,就是如果配置的選項都沒有抽中,我們該做什么處理,init函數里面我們定義了一個數組,保存了配置選項之外的選項數組,如果沒有抽中配置的選項,我們從剩下的選項中隨機抽中一個。
getNum(chance) {
let n1 = Math.round(Math.random() * 100);
let start = 0, end = 0;
for (let key in chance) {
end = start + parseInt(chance[key]);
if (n1 > start && n1 < end) {
return key;
}
start = end;
}
//如果配置的概率沒有抽中,則剩下的商品概率評分
let rad = Math.floor(Math.random() * (this.residue.length));
return this.residue[rad];
}
接下來就比較簡單的,我們就可以操作轉動的元素,以及給這個元素設置動畫,這里我們用的是css3 過渡屬性,所以實現起來比較簡單,也不需要寫定時器了。
rotate(el, count, btn, innerTime, endFunc) {
var endPos = (count * 360 - this.deg / 2) + (this.pos * this.deg) + Math.random() * this.deg / 2 + 10;
var btn = $_(btn);
var el = $_(el);
btn.onclick = () => {
this.aniamte(el, endPos,innerTime);
}
el.addEventListener("transitionend", this.end.bind(this, endFunc), false);
}
aniamte(el, endPos, innerTime) {
el.style.transform = "rotate(" + endPos + "deg)";
el.style.transition = "all " + innerTime + "ms linear";
}
end(endFunc) {
endFunc && endFunc(this.pos);
}
最后我們在動畫結束后可以傳入一個回調函數,這樣我們用的時候就不用改內部的代碼,在調用插件的時候就可以做一些操作。最后附上全部的代碼,方便大家理解:
(function () {
function $_(name) {
return document.querySelector(name);
}
class Draw {
constructor(opt) {
let defaultopt = {
el: ".zd_round img",//轉動的元素
btn: ".pointer", //點擊轉動的元素
count: 3, //轉盤轉動的圈數,
chance: {
1: "40%",
5: "40%", //指定抽獎概率,其余則平分
},
num: 8,//轉盤的總個數
innerTime: 4000 //轉盤轉動的時間
}
opt = Object.assign({}, defaultopt, opt);
this.init(opt);
}
init(opt) {
let { el, chance, count, num, btn, innerTime ,endFunc} = opt;
let numArray = Object.keys(chance);
this.residue = [];
for (let i = 0; i < num; i++) {
if (!numArray.includes(String(i))) {
this.residue.push(i)
}
}
this.pos = this.getNum(chance); //確定轉盤最后停留的位置
this.deg = 360 / num;
this.rotate(el, count, btn, innerTime,endFunc);
}
getNum(chance) {
let n1 = Math.round(Math.random() * 100);
let start = 0, end = 0;
for (let key in chance) {
end = start + parseInt(chance[key]);
if (n1 > start && n1 < end) {
return key;
}
start = end;
}
//如果配置的概率沒有抽中,則剩下的商品概率評分
let rad = Math.floor(Math.random() * (this.residue.length));
return this.residue[rad];
}
rotate(el, count, btn, innerTime,endFunc) {
var endPos = (count * 360 - this.deg / 2) + (this.pos * this.deg) + Math.random() * this.deg / 2 + 10;
var btn = $_(btn);
var el = $_(el);
btn.onclick = () => {
this.aniamte(el, endPos,innerTime);
}
el.addEventListener("transitionend", this.end.bind(this,endFunc), false);
}
aniamte(el, endPos, innerTime) {
el.style.transform = "rotate(" + endPos + "deg)";
el.style.transition = "all " + innerTime + "ms linear";
}
end(endFunc) {
endFunc&&endFunc(this.pos);
}
}
window.Draw=Draw;
})();
最后說一個坑,可能之前數學學多了,我們知道數學里面可以寫一個變量大於小於某個數值,比如3<n<5,這個表示的是大於3小於5的一個數字,然后我在if判斷也這樣寫,發現返回都是true.

我覺得還是老老實實的用&吧。
n1 > start && n1 < end
代碼可以去我的github下載 https://github.com/shentaochok/Draw.git
寄語:怕,你就會輸一輩子!
