为了账号安全,请及时绑定邮箱和手机立即绑定

原生(纯)js+html+css实现移动端抽奖转盘系统

这是我前个月使用纯javascript+html写出的一个抽奖转盘系统,按理来说,我应该在当时做完这个小系统,就应该立即写bike总结才对,但是本人之前没有在网上写博客的习惯,平时总结更加习惯写在纸上,但是现在发现卸载网上可能更好。博客中有许多高手,当你们点进来看到我这篇小总结的时候,还希望多多给我提出建议咯,感谢!其他的话不多说啦,下面进入正题,先亮出我转盘的庐山正面目(ps:这张图是我在网上随便找的,只是简单处理了一下水印)

我的抽奖转盘是水平垂直居中的,也就是处于手机屏幕的正中间,先贴上我html部分的代码

 <div class="random" id="randomDiv">
<div class="content" id="content">
</div>
<div class="start" id="start">start</div>
<div class='close' id="close"> 恭喜中奖</div>
</div>
 <div class="down" id='down'></div>

再贴上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<len1;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页面中加上:

<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/touch.js"></script>
<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/random.js"></script>
<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/main.js"></script>
<script>
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);

</script>

好啦,贴出这么多代码,相信大家也没耐心看下去。先让我来讲讲我的主要思路吧。我在做这个转盘的时候,是希望能够作为一个插件来使用。所以基于考虑抽奖系统的可复用性,一开始我做的时候,运用的是设计模式的单一职责原则,尽量把职责单一化,粒度化,所以我将这个系统主要分为三个对象,random对象(转盘对象)和TouchEvent对象(触摸对象)和obj对象(传入的dom对象需要用到的方法),而且将它们分别放在random文件和touch文件中和main文件中,便于管理。

random对象需要考虑转盘中一共有多少种奖项,在我这里是四种,每种奖项有几个色块。最开始我做的时候,思路很单一,很直接地将表面看到的东西写出来,没有考虑人为可控地自定义每个奖项发生的概率,而是直接由给出图片的来决定,换句话说,图片上每个奖项有几个色块,色块大小有多少决定了奖项被抽中的概率。这样其实不是很好,设计人员给的图片已经是定死的,如何灵活的自定义概率,前端其实就像变魔术,我们只需要将最终需要呈现的效果实现就好,过程可以更加有趣一点。于是,我不看转盘图片上有每个奖项有几个色块,直接定义每个奖项的被抽中的概率范围,这里我是将一等奖设置在0到0.1之间,每次随机生成一个小于1的随机数,看看随机数处于哪个区间,这样决定被抽中的奖项。既然要做的逼真一些,那么就不能固定转到同一个色块,让指针最终停留在被抽的奖项的任意一个随机的色块。还有一个问题是,开始我让转盘匀速转动,给定它一定时间,达到时间后直接挺下来。但这样设定,不符合现实中的场景,我们转动一个转盘,肯定是让它速度越来越慢,最终停下来。所以转盘的转动由时间来控制,最终变成初始速度来决定。

我这里的touch对象是转盘正中间的圆形按钮,如果按钮上的文本是start,此时点击,转盘会转动,文本由start变成close,当转盘是停下来,文本又变回start。

我这里的方法全部都是封装好的,我在这里,自定义了window.$attrCommon = function(){};用来暴露对外接口,除了对象的属性和方法,其他的方法全部(function()).apply($attrCommon),这样既可以避免全局作用域被污染,还能封装,使用apply,也可以降低方法和对象的耦合度。

点击查看更多内容
6人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
8
获赞与收藏
58

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消