这是我前个月使用纯javascript+html写出的一个抽奖转盘系统,按理来说,我应该在当时做完这个小系统,就应该立即写bike总结才对,但是本人之前没有在网上写博客的习惯,平时总结更加习惯写在纸上,但是现在发现卸载网上可能更好。博客中有许多高手,当你们点进来看到我这篇小总结的时候,还希望多多给我提出建议咯,感谢!其他的话不多说啦,下面进入正题,先亮出我转盘的庐山正面目(ps:这张图是我在网上随便找的,只是简单处理了一下水印)
我的抽奖转盘是水平垂直居中的,也就是处于手机屏幕的正中间,先贴上我html部分的代码
再贴上css代码
*{
padding: 0;
margin: 0;
}
html{
position: absolute;
overflow: hidden;
}
body{
background-color: white;
overflow: hidden;
}
.random{
width:14.6rem;
height:14.6rem;
position:absolute;
margin: auto;
}
.content{
width:14.6rem;
height:14.6rem;
background: url(bg.jpg);
background-repeat: no-repeat;
background-size: 100%;
background-orign:content-box;
transform:rotate(0deg);
}
.down{
width:2rem;
height: 0.7rem;
background-image: url(down.png);
background-size: 100%;
position: absolute;
left:7rem;
top:0;
}
.start{
width:3rem;
height:3rem;
background-color: red;
position:absolute;
top:5.8rem;
left:5.8rem;
border-radius: 4rem;
-o-border-radius:4rem;
text-align: center;
line-height:3rem;
}
.close{
position: absolute;
top:3rem;
width:4rem;
left:5rem;
text-align: center;
font-weight: bold;
background-color: white;
font-size: 1rem;
display: none;
}
前面的css+html部分的代码只是将我的转盘最安静的时候展现出来
使用原生JavaScript才能真正展现它动如脱兔的一面,先将我的代码贴出来,再讲讲我的思路吧!
random.js文件:
//转盘对象
var Rondom = function(){}
//返回随机的度数
Rondom.prototype.getDeg = function(arr){
var arr = arr;
var len1 = arr.length;//一共有几种奖项
var len2 = 0;//每种奖项有几种可能
var rom2;//抽取奖项的其中一个色块
var deg;//指针只指向的位置
var type;//抽取到的奖品类型
//获取随机数
var rom1 = Math.random();
for(var i = 0; i
if(arr[i].pro >= rom1){
len2 = arr[i].deg.length;
rom2 = Math.round(Math.random() * (len2 -1));
deg = arr[i].deg[rom2];
type = arr[i].type;
break;
}
}
return {'type': type,//返回奖品类型
'deg':deg //返回指针停下来的位置
}
;
}
//转盘旋转
Rondom.prototype.turn = function(objc,objc2,objc3,objc4){
//获取transform的当前旋转位置数组
var arr = ($attrCommon.getStyle(objc,"transform")).split(',');
var arrCos = parseFloat(arr[3]);
var arrSin = parseFloat(arr[1]);
var sinDeg = Math.round(Math.asin(arrSin)*(180/Math.PI));//求当前转盘可能的角度
var cosDeg = Math.round(Math.acos(arrCos)*(180/Math.PI));//求当前转盘可能的角度
var deg = 0;//实际角度
if(arrCos*arrSin > 0){
arrSin > 0 ? deg = cosDeg : deg = 180 - sinDeg;
}else if(arrCos*arrSin < 0){
arrCos > 0 ?deg = sinDeg + 360 : deg = cosDeg;
}else{
arrSin >= 0 ? deg = cosDeg : deg = sinDeg + 360 ;
}
if(objc4){
//$attrCommon.setRotate(objc,objc3,deg);
return deg;
}else{
$attrCommon.setRotate(objc,objc3,deg);
}
}
//向TouchEvent对象订阅start触摸事件
Rondom.prototype.listenerStart = (function(){
window.timeId1;//定义计时器
var n = 0;//计时
var flag = true;//是否点击了转盘
var deg;
var deg2 = 0;//获取上次旋转停留的位置
var m = 0;//旋转次数,可以为小数
var posObj;//获取抽取到的奖项对象
return function(objc,n,objc2,objc3,objc4){
var $this = this;//保存对this的引用
timeId1 = setInterval(function(){
if(flag){
posObj = $this.getDeg(objc4);
deg2 = $this.turn(objc,objc4,posObj.deg,true);//获取上次旋转停下来的位置
objc.style.transform = 'rotate('+0+'deg'+')';//将转盘转回最初的位置
deg = posObj.deg + 720;//转盘每次点击,一共旋转多少度
flag = false;
}
deg2 = $this.turn(objc,objc4,posObj.deg);
n = n + 0.2;
m = m + ((720+posObj.deg)/posObj.deg);//转盘目前旋转了多少度
clearInterval(timeId1);
if(m <= (720+posObj.deg)){
if((m <= 720)){
$this.listenerStart(objc,n,objc2,objc3,objc4);
}else if(m = (720+ posObj.deg)){
deg2 = $this.turn(objc,objc4,posObj.deg,true);//转盘在最后一次旋转前的位置
objc.style.transform = 'rotate('+ 0 +'deg'+')';//将转盘转回初始位置
$this.listenerStart(objc,n,objc2,objc3,objc4);//转盘旋转到指定的角度上
}
}else{
flag = true;
n = 0;
m = 0;
deg2 = 0;
deg = 0;
$this.listenerEnd(objc3,objc2,posObj.type); //结束旋转
posObj = null;
}
},n);
}
})();
//向TouchEvent对象订阅close事件
Rondom.prototype.listenerEnd = function(obj1,obj2,text){
var timeId2 = setTimeout(function(){
obj1.style.display = 'block';
obj2.textContent = 'start';
obj1.textContent = text;
},500);
}
var random = new Rondom();
main.js文件:
//给rem赋予新的值,实现自适应布局
(function (doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
docEl.style.fontSize = 20 * (clientWidth / 320) + 'px';
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
window.$attrCommon = function(){};//暴露对外接口
(function(){
//读出obj对象的attr属性值
this.getStyle = function(obj,attr)
{
if(obj.currentStyle)
{
return obj.currentStyle[attr];
}
else
{
return document.defaultView.getComputedStyle(obj,false)[attr];
}
},
//给obj对象的transform属性赋值
this.setRotate = function(obj,obj2,obj3){
var n = obj3 + obj2;//转幅
var cosVal = Math.cos(n * Math.PI / 180);//求出旋转角度对应的cos值
var sinVal = Math.sin(n * Math.PI / 180);//求出旋转角度对应的sin值
obj.style.transform = 'matrix(' + cosVal + ','+ sinVal + ','
+ (-sinVal) + ',' + cosVal + ',' + 0 + ',' + 0 + ')';//obj对象旋转
}
}).apply($attrCommon);//对象冒充,降低方法与对象耦合度
(function(){
this.init = function(e,objc1,objc2,objc3,objc4){
try{
var val = touchEvent.touchend(e,objc2);
if(val == 'start'){
random.listenerStart(objc1,1,objc3,objc2,objc4);
}
}
catch(err){
alert('发生错误!');
}
}
}).apply($attrCommon);
(function(){
this.setTop = function(objc1,objc2){
var docEl = document.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
var height1 = parseFloat(($attrCommon.getStyle(objc1,'height')).split('p')[0]);
var height2 = parseFloat(($attrCommon.getStyle(objc2,'height')).split('p')[0]);
var width1 = parseFloat(($attrCommon.getStyle(objc1,'width')).split('p')[0]);
var width2 = parseFloat(($attrCommon.getStyle(objc2,'width')).split('p')[0]);
var clientHeight = docEl.clientHeight;
var clientWidth = docEl.clientWidth;
if (!clientHeight || !clientWidth) return;
objc1.style.top = (clientHeight/2- height1/2) + 'px';
objc2.style.top = (clientHeight/2 - height2/2) + 'px';
objc1.style.left = (clientWidth/2- width1/2) + 'px';
objc2.style.left = (clientWidth/2 + width2/4) + 'px';
};
if (!document.addEventListener) return;
window.addEventListener(resizeEvt, recalc, false);
document.addEventListener('DOMContentLoaded', recalc, false);
}
}).apply($attrCommon);
最后在html页面中加上:
var start = document.getElementById('start');
var randomDiv = document.getElementById('randomDiv');
var content = document.getElementById('content');
var close = document.getElementById('close');
var down = document.getElementById('down');
//转盘中一共有四种可能性,每种可能性的概率为0.1,0.2,0.3,0.4,一共为1
var arr = [{'pro': 0.1,'deg': [96,316],'type': '一等奖'},{'pro': 0.3,'deg': [40,274,176],'type':'二等奖'},
{'pro': 0.6,'deg': [18,297,197,117],'type':'三等奖'},{'pro': 1,'deg': [68,342,236,148],'type':'幸运奖'}];
//content.style.transform = 'rotate(316deg)';
var $attrcommon = new $attrCommon();//实例化random.js接口
start.addEventListener('touchend',function(e){
$attrCommon.init(e,content,close,start,arr);
},false);
$attrCommon.setTop(randomDiv,down);
好啦,贴出这么多代码,相信大家也没耐心看下去。先让我来讲讲我的主要思路吧。我在做这个转盘的时候,是希望能够作为一个插件来使用。所以基于考虑抽奖系统的可复用性,一开始我做的时候,运用的是设计模式的单一职责原则,尽量把职责单一化,粒度化,所以我将这个系统主要分为三个对象,random对象(转盘对象)和TouchEvent对象(触摸对象)和obj对象(传入的dom对象需要用到的方法),而且将它们分别放在random文件和touch文件中和main文件中,便于管理。
random对象需要考虑转盘中一共有多少种奖项,在我这里是四种,每种奖项有几个色块。最开始我做的时候,思路很单一,很直接地将表面看到的东西写出来,没有考虑人为可控地自定义每个奖项发生的概率,而是直接由给出图片的来决定,换句话说,图片上每个奖项有几个色块,色块大小有多少决定了奖项被抽中的概率。这样其实不是很好,设计人员给的图片已经是定死的,如何灵活的自定义概率,前端其实就像变魔术,我们只需要将最终需要呈现的效果实现就好,过程可以更加有趣一点。于是,我不看转盘图片上有每个奖项有几个色块,直接定义每个奖项的被抽中的概率范围,这里我是将一等奖设置在0到0.1之间,每次随机生成一个小于1的随机数,看看随机数处于哪个区间,这样决定被抽中的奖项。既然要做的逼真一些,那么就不能固定转到同一个色块,让指针最终停留在被抽的奖项的任意一个随机的色块。还有一个问题是,开始我让转盘匀速转动,给定它一定时间,达到时间后直接挺下来。但这样设定,不符合现实中的场景,我们转动一个转盘,肯定是让它速度越来越慢,最终停下来。所以转盘的转动由时间来控制,最终变成初始速度来决定。
我这里的touch对象是转盘正中间的圆形按钮,如果按钮上的文本是start,此时点击,转盘会转动,文本由start变成close,当转盘是停下来,文本又变回start。
我这里的方法全部都是封装好的,我在这里,自定义了window.$attrCommon = function(){};用来暴露对外接口,除了对象的属性和方法,其他的方法全部(function()).apply($attrCommon),这样既可以避免全局作用域被污染,还能封装,使用apply,也可以降低方法和对象的耦合度。
打开App,阅读手记