JavaScript实现跑马灯抽奖活动实例代码解析与优化(二)
在上篇文章给大家介绍了JavaScript实现跑马灯抽奖活动实例代码解析与优化(一),既然是要编写插件。那么叫做“插件”的东西肯定是具有的某些特征能够满足我们平时开发的需求或者是提高我们的开发效率。那么叫做插件的东西应该具有哪些基本特征呢?让我们来总结一下:
1.JavaScript插件一些基本特征:
配置一定要简单
插件中定义的变量不污染全局变量;
同一段代码可以在不同的地方复用;
用户可以自定义自己功能参数;
具有销毁变量和参数的功能;
如果按照以上的几个特征来写插件的话,我们可以总结出一个基本的代码结构,我们一个一个的来看:
1.插件配置要尽可能的简单
html中配置容器节点
//这里的node-type="reward-area"是标识我们插件的容器节点 <divclass="re-area"node-type="reward-area">
DOM加载完成以后初始化插件
$(function(){ //这里的test是代表容器的class window.LightRotate.init($('[node-type=reward-area]')); });
2.插件中定义的变量不污染全局变量
JavaScript具有块级作用域的标识符就是function了。那我们怎么声明我们的变量才可以使它不污染全局变量呢?
这里我们需要用到的一个JavaScript函数的自执行的知识点。代码如下:
(function(){ //dosomething })();
3.在不同的地方复用功能代码
这就要用到我们面向对象的知识点,把我们的功能代码抽象成对象,在我们需要使用的时候,实例化对象就可以了。那我们接着第二部的代码继续写,
// (function($){ //创建功能对象 varLightRotate=function(select){ //dosomething }; LightRotate.init=function(select){ var_this=this; //根据不同的容器实例化不同的对象 select.each(function(){ new_this($(this)); }); }; window.LightRotate=LightRotate; })(jQuery);
4.用户可以自定义功能参数
首先我们应该有默认的参数设定,比如下面这样
// (function($){ //创建功能对象 varLightRotate=function(select){ //自定义的参数 this.setting={ liAutoPlay:false,//周围的灯是否自动旋转 roLiSpeed:100,//灯旋转的速度ms roPrSpeed:200,//奖品旋转速度ms liDirection:true,//旋转方向true正方向false反方向 randomPrize:false//空格是否随机选取 }; }; LightRotate.init=function(select){ var_this=this; //根据不同的容器实例化不同的对象 select.each(function(){ new_this($(this)); }); }; window.LightRotate=LightRotate; })(jQuery);
其实这样写的话,使用者已经可以修改我们的JavaScript文件来完成自定义了。但是为了能够让我们的差价足够的好用,比如说,我们的使用者一点儿都不懂js呢?该怎么办?
这样我们可以把这些参数用自定义属性配置在html中,如下:
<divclass="re-area"node-type="reward-area"data-setting='{ "liAutoPlay":false, "roLiSpeed":100, "roPrSpeed":200, "liDirection":true, "randomPrize":false}'>
这样用户只需要在html的节点中就可以配置当前容器运行的参数。这样的好处还可以使同一页面上的不同容器,可以单独的配置参数,减少耦合。
那么在js中我们该怎么获取这些参数呢?在上面的代码中,已经有了功能对象函数。那么我们想扩展对象方法来获取用户的自定义参数,怎么办呢?我们一般使用prototype的东西来扩展我们已有对象的方法,代码如下:
// (function($){ //创建功能对象 varLightRotate=function(select){ //自定义的参数 this.setting={ liAutoPlay:false,//周围的灯是否自动旋转 roLiSpeed:100,//灯旋转的速度ms roPrSpeed:200,//奖品旋转速度ms liDirection:true,//旋转方向true正方向false反方向 randomPrize:false//空格是否随机选取 }; //这里调用对象的获取用户自定义参数的方法,并且将默认参数合并 $.extend(_this.setting,_this.getSettingUser()); }; LightRotate.prototype={ //扩展获取用户自定义参数的方法 getSettingUser:function(){ varuserSetting=this.LightArea.attr('data-setting'); if(userSetting&&userSetting!==''){ return$.parseJSON(userSetting); }else{ return{}; } } }; LightRotate.init=function(select){ var_this=this; //根据不同的容器实例化不同的对象 select.each(function(){ new_this($(this)); }); }; window.LightRotate=LightRotate; })(jQuery);
5.销毁变量和参数的功能;
最后一个就是我们的插件应该具有销毁自身变量和参数的功能。我们该怎么写呢?还是在上面的代码基础上继续扩展功能对象的可调用方法,代码如下:
LightRotate.prototype={ //扩展获取用户自定义参数的方法 getSettingUser:function(){ varuserSetting=this.LightArea.attr('data-setting'); if(userSetting&&userSetting!==''){ return$.parseJSON(userSetting); }else{ return{}; } }, //销毁对象参数 destory:function(){ $(_this.LightArea).off(); this.closeAnimation(); this.rewardTimer=null; } };
由以上我们的内容我们可以大概了解了一个成熟的插件应该具有的基本功能。
2.插件开发和优化示例
刚好这个项目是在春节放假前的一个紧急的项目,当时为了赶进度就没有详细思考自己的代码结构,这样野味自己的后续优化提供了机会。
由上一节介绍的定时器的内容可以知道JavaScript是单线程的。所以
如果一段代码运行效率很低,就会影响后续代码的执行。所以对于JavaScript,代码优化是必须的。
先来看看我们的“跑马灯”插件应该具有哪些功能:
能够控制灯是否自动播放;
灯的旋转方向可以控制;
灯的旋转速度可以控制;
奖品的旋转速度可以控制;
这里就不详细的介绍这些功能点的开发过程,仅仅介绍优化过程。如果有兴趣可以看我文章最后附上的源代码地址,进行下载阅读。
1.“顺序”获取旋转灯代码的优化
因为周围的灯我是使用绝对定位来做的,所以我需要“顺序”的获取他们的列表,然后操作。
首先获取DOM节点。
//获取外围的灯,可以看到我这里使用的选择器多了一个select,是为了获取当前容器下的某些元素,避免有多个容器存在时冲突 this.topLight=$('[node-type=re-top]',select).find('span'); this.rightLight=$('[node-type=re-right]',select).find('span'); this.bottomLight=$('[node-type=re-bottom]',select).find('span'); this.leftLight=$('[node-type=re-left]',select).find('span');
然后就应该“顺序”的获取“灯”节点的DOM元素列表。
我的第一版是这样做的:
Zepto(topLight).each(function(){ lightList.push(this); }); Zepto(rightLight).each(function(){ lightList.push(this); }); for(varj=bottomLight.length-1;j>=0;j--){ lightList.push(bottomLight[j]); } for(varm=leftLight.length-1;m>=0;m--){ lightList.push(leftLight[m]); }
因为“下”和“左”方向的灯是需要倒序的,所以我使用了两个倒序的for循环,其实当循环出现的时候,我们都应该思考我们的代码是否有可优化的空间。
优化后的代码是这样子的,在这里我减少了4次循环的使用
function(){ varlightList=[]; varbottomRever; varleftRever; bottomRever=Array.from(this.bottomLight).reverse(); leftRever=Array.from(this.leftLight).reverse(); lightList=Array.from(this.topLight).concat(Array.from(this.rightLight)); lightList=lightList.concat(bottomRever); lightList=lightList.concat(leftRever); }
列表倒序我使用了原生Array对象的reverse方法。
2.使用“闭包”优化顺序循环播放
为了能够使我们的“灯”顺序的跑起来,第一版的思路是:
给每一个“灯”(注意,这里是每一个,罪过…罪过…)定义一个setTimeout(),执行时间就是数序的加入js执行队列中去。
代码是下面这样子的:
varzepto_light=Zepto(lightList); varchangeTime=100; varlightLength=zepto_light.length; vartotleTime=changeTime*lightLength; functionlightOpen(){ for(vari=0;i<lightLength;i++){ (functiontemp(i){ lightTimer=setTimeout(function(){ if(stopAnimation===false){ Zepto(zepto_light).removeClass('light_open'); Zepto(zepto_light[i]).addClass("light_open"); }else{ return; } },changeTime*i); })(i); } }
这样子写的缺点很明显:如果我有100个“灯”那么就会在当前的js执行队列中加入100个setTimeout(),再次强调的是我这里又使用了for循环,在时间复杂度上又增加了。代码的执行效率又下降了。
后来思考了下,JavaScript中“闭包”符合我当前的使用场景,就想着用闭包优化一下,优化后代码如下:
lightRun:function(){ var_this=this; functiontempFunc(){ varlightList=_this.getLightList(); varlightLength=lightList.length; vari=0; returnfunction(){ $(lightList,_this.LightArea).removeClass('light_open'); $(lightList[i],_this.LightArea).addClass("light_open"); i++; //使一轮循环结束后能够继续下次循环 if(i===lightLength){ i=0; } }; } varlightRunFunc=tempFunc(); lightRunFunc(); _this.lightInterVal=setInterval(lightRunFunc,_this.setting.roLiSpeed); }
由以上的代码可以很明显的发现两个优点:第一,就是减少了for循环的使用,降低了代码的时间复杂度,第二就是,每次我仅仅在当前代码执行的队列中创建一个setInterval()。减小了执行队列的复杂度。
关于JavaScript实现跑马灯抽奖活动实例代码解析与优化(二)的相关知识就给大家介绍到这里,希望本文所述对大家有所帮助。