【项目分析】设计一个计时器的jQuery插件(4)(附赠:中秋节祝福)

背景

目前团队所负责的公务员考试的项目中,需要使用到计数器的前端JS组件,具体应用可以查看页面

1

你会发现这里的计时器代码有些问题。

这里使用的是

ExpandedBlockStart.gif 代码
this ._timerId  =  window.setInterval( function () { 
                    $
this ._step(); 
                }, 
1000 );

//

_step: 
function () { 
           
if  ( this ._pattern  ==   ' down '
               
this ._currentTime  -=   1
           
else  
               
this ._currentTime  +=   1
           
// 每秒执行一次回调函数 
            if  ( this ._currentTime  ==   0   ||   this ._currentTime  >   this ._totalTime) { 
               
this .Stop(); 
           } 
           
else  { 
               
this ._runCallBack( this ._onInterval); 
           } 
       }

 

可以发现,这里在触发1秒的时候,使用的是直接增加1秒或者减少1秒的方式进行。这样是不合理的。我们可以发现我们在浏览器上选择右键,这里的计时器会被停止,当你下次再触发事件的时候,时间就不准确了。这样,只要用户点击右键,就能够把计时器给停掉。有的人说,可以屏蔽右键,但是并不是所有浏览器,都能够支持屏蔽右键,并且还有其他的方式能够使计时器停止;或者,可以使用flash来实现的计时器的功能,嗯,这样是可以的,只是我们的开发人员对于flash不是很熟悉,于是想还是自己重新设计下这个JS的计时器组件。

于是我这里采用了jQuery插件的方式来重写这个计时器组件。

 

具体分析

1. 首先先来看下最后的调用方法是什么样的:

ExpandedBlockStart.gif 代码
< div  id ="counter-1" >  
</ div >

< div  id ="counter-2" >  
</ div >

$('#counter-1').counter({ 'pattern': 'down', 'totalTime': 3600, 'onInterval': fnInterval, 'onStop': fnStop }); 
$('#counter-2').counter({ 'pattern': 'up' });

 

通过代码可以很直观的看到实现的方式,其中pattern是计时器的方式(累减或者累加),totalTime是倒计时总时间,onInterval是每跳动一次计时器,需要触发的事件,onStop是等计时器倒计时完毕后触发的事件。

 

2. 然后,来看看jquery.counter.js中的核心代码:

ExpandedBlockStart.gif 代码
/*  主函数  */  
$.fn.counter 
=   function (options) { 
    
var  args  =  Array.prototype.slice.call(arguments,  1 ); 

    
return   this .each( function () { 
        
if  ( typeof  options  ==   ' string ' ) { 
            $.counter[
' _ '   +  options  +   ' Counter ' ].apply($.counter, [ this ].concat(args)); 
        } 
        
else  { 
            $.counter._attachCounter(
this , options); 
        } 
    }); 
};

 

这里的options是Object类型的,所以调用 $.counter._attachCounter(this, options)。

接着看:

ExpandedBlockStart.gif 代码
/*  绑定计数器  */  
_attachCounter: 
function (target, options) { 
    
var  inst  =  { options: $.extend({  ' from ' new  Date() }, options) }; 
    $.data(target, DATA_NAME, inst); 
    
this ._changeCounter(target); 
}

 

这里把inst的选项数据作为一个数据对象在客户端保存起来 $.data(target, DATA_NAME, inst);

/*  重置计数器  */  
_changeCounter: 
function (target) { 
    
this ._addTarget(target); 
    
this ._updateCounter(target); 

}

 其中_addTarget是为了把目标的jQuery对象放在一个目标计时器列表_timerTargets中去维护。

ExpandedBlockStart.gif 代码
/*  重新显示计数器  */  
_updateCounter: 
function (target) { 

    
var  remainTime  =   this ._getTime(target); 
    
if  (remainTime) { 
        
// 回调函数调用 
         var  inst  =  $.data(target, DATA_NAME); 
        
if  (remainTime  >=   0 ) { 
            
var  time  =   this ._getFormatTime(remainTime); 
            $(target).html(time); 

            
var  onInterval  =   this ._get(inst,  ' onInterval ' ); 
            
if  (onInterval) { 
                onInterval.apply(target, [remainTime]); 
            } 
        } 
        
else  { 
            remainTime 
=   0
            
var  time  =   this ._getFormatTime(remainTime); 
            $(target).html(time); 

            
var  onStop  =   this ._get(inst,  ' onStop ' ); 
            
if  (onStop) { 
                onStop.apply(target, []); 
                
this ._removeTarget(target); 
            } 
        } 
    }

这里是前端显示的核心代码,onInterval,onStop会触发相关的事件;

通过from的起始设置的时间,来每次触发时候相减来计算剩余时间,而不是像前面那种JS组件那边,每隔一秒加(减)1。

 

3. 具体计时的是在何时触发的呢?

$.extend(Counter.prototype, { 
    
/*  共享所有的timer  */  
    _timer: setInterval(
function () { $.counter._updateTargets(); },  900 ), 
    
/*  当前激活的timer列表  */  
    _timerTargets: [],

    …

});

 

这里每隔900毫秒会执行一次事件(可以根据您的情况来设置)

ExpandedBlockStart.gif 代码
var  remainTime  =   this ._getTime(target);可以获得剩余的时间;

/*  获取当前计数器的时间秒数  */  
_getTime: 
function (target) { 
    
var  inst  =  $.data(target, DATA_NAME); 
    
if  (inst) { 
        
var  pattern  =   this ._get(inst,  ' pattern ' ); 
        
if  (pattern  ==   ' down ' ) { 
            
var  totalTime  =   this ._get(inst,  ' totalTime ' ); 
            
var  from  =   this ._get(inst,  ' from ' ); 
            
var  nowDate  =   new  Date(); 
            
var  remainTime  =  parseInt((totalTime  *   1000   -  (nowDate.getTime()  -  from.getTime()))  /   1000 ); 
            
return  remainTime; 
        } 
        
else   if  (pattern  ==   ' up ' ) { 
            
var  from  =   this ._get(inst,  ' from ' ); 
            
var  nowDate  =   new  Date(); 
            
var  remainTime  =  parseInt((nowDate.getTime()  -  from.getTime())  /   1000 ); 
            
return  remainTime; 
        } 
    } 
    
return   null
}

 

4. 通过getTime方法来获取剩余时间:

ExpandedBlockStart.gif 代码
/*  获取计时器当前时间(总秒数)  */  
$.fn.getTime 
=   function () { 
    
var  args  =  Array.prototype.slice.call(arguments,  1 ); 
    
if  ( this .length  ==   1 ) { 
        
return  $.counter[ ' _getTime ' ].apply($.counter, [ this [ 0 ]].concat(args)); 
    } 
    
else   if  ( this .length  >   1 ) { 
        
var  array  =  []; 
        
this .each( function () { 
        
var  time  =  $.counter[ ' _getTime ' ].apply($.counter, [ this ].concat(args)); 
            
if  (time) { 
                array.push(time); 
            } 
        }); 
        
return  array; 
    } 
    
return   null
};

 

调用方式为:

$('#counter-1').getTime()

 

5. 通过string参数来实现暂停和继续的功能:

$( ' #btnPause ' ).toggle( function () { 
    $(
this ).val( ' 继续 ' ); 
    $(
' .counter ' ).counter( ' pause ' ); 
}, 
function () { 
    $(
this ).val( ' 暂停 ' ); 
    $(
' .counter ' ).counter( ' resume ' ); 

);

 

 

其他的请看完整代码:

ExpandedBlockStart.gif 代码
/*
* 计数器jQuery插件
* Copyright: Leepy
* Update: 2010-09-22
* Home:   http://www.cnblogs.com/liping13599168/
*/

(
function ($) {

    
function  Counter() {
        
/*  计数器默认配置  */
        
this ._defaults  =  {
            pattern: 
' down ' ,     //  可选择参数:'up', 'down';默认方式为减计数
            totalTime:  3600 ,     //  总共需要多少时间,单位为秒
            until:  null ,         //  默认直到日期的配置
            onInterval:  null ,    //  间隔时间回调函数
            onStop:  null          //  结束时回调函数
        }
    }

    
var  DATA_NAME  =   ' data_counter ' ;

    $.extend(Counter.prototype, {
        
/*  共享所有的timer  */
        _timer: setInterval(
function () { $.counter._updateTargets(); },  900 ),
        
/*  当前激活的timer列表  */
        _timerTargets: [],
        
/*  更新每一个绑定的目标计数器  */
        _updateTargets: 
function () {
            
for  ( var  i  =   0 , length  =   this ._timerTargets.length; i  <  length; i ++ ) {
                
this ._updateCounter( this ._timerTargets[i]);
            }
        },
        
/*  绑定计数器  */
        _attachCounter: 
function (target, options) {
            
var  inst  =  { options: $.extend({  ' from ' new  Date() }, options) };
            $.data(target, DATA_NAME, inst);
            
this ._changeCounter(target);
        },
        
/*  重置计数器  */
        _changeCounter: 
function (target) {
            
this ._addTarget(target);
            
this ._updateCounter(target);

        },
        
/*  重新显示计数器  */
        _updateCounter: 
function (target) {

            
var  remainTime  =   this ._getTime(target);
            
if  (remainTime) {
                
// 回调函数调用
                 var  inst  =  $.data(target, DATA_NAME);
                
if  (remainTime  >=   0 ) {
                    
var  time  =   this ._getFormatTime(remainTime);
                    $(target).html(time);

                    
var  onInterval  =   this ._get(inst,  ' onInterval ' );
                    
if  (onInterval) {
                        onInterval.apply(target, [remainTime]);
                    }
                }
                
else  {
                    remainTime 
=   0 ;
                    
var  time  =   this ._getFormatTime(remainTime);
                    $(target).html(time);

                    
var  onStop  =   this ._get(inst,  ' onStop ' );
                    
if  (onStop) {
                        onStop.apply(target, []);
                        
this ._removeTarget(target);
                    }
                }
            }

        },
        
/*  暂停计数器  */
        _pauseCounter: 
function (target) {
            
var  inst  =  $.data(target, DATA_NAME);
            
if  (inst) {
                
var  pauseTime  =   new  Date();
                $.extend(inst.options, { 
' pauseTime ' : pauseTime });
                $.data(target, DATA_NAME, inst);
                
this ._removeTarget(target);
            }
        },
        
/*  重新启动计数器  */
        _resumeCounter: 
function (target) {
            
var  inst  =  $.data(target, DATA_NAME);
            
if  (inst) {
                
var  nowDate  =   new  Date();
                
var  pauseTime  =   this ._get(inst,  ' pauseTime ' );
                
var  from  =   this ._get(inst,  ' from ' );
                
if  (pauseTime) {
                    
var  fromTime  =   new  Date(from.getTime()  +  (nowDate.getTime()  -  pauseTime.getTime()));
                    $.extend(inst.options, { 
' from ' : fromTime });
                    $.data(target, DATA_NAME, inst);
                    
this ._changeCounter(target);
                }
            }
        },
        
/*  获取当前计数器的时间秒数  */
        _getTime: 
function (target) {
            
var  inst  =  $.data(target, DATA_NAME);
            
if  (inst) {
                
var  pattern  =   this ._get(inst,  ' pattern ' );
                
if  (pattern  ==   ' down ' ) {
                    
var  totalTime  =   this ._get(inst,  ' totalTime ' );
                    
var  from  =   this ._get(inst,  ' from ' );
                    
var  nowDate  =   new  Date();
                    
var  remainTime  =  parseInt((totalTime  *   1000   -  (nowDate.getTime()  -  from.getTime()))  /   1000 );
                    
return  remainTime;
                }
                
else   if  (pattern  ==   ' up ' ) {
                    
var  from  =   this ._get(inst,  ' from ' );
                    
var  nowDate  =   new  Date();
                    
var  remainTime  =  parseInt((nowDate.getTime()  -  from.getTime())  /   1000 );
                    
return  remainTime;
                }
            }
            
return   null ;
        },
        
/*  获取格式化的时间  */
        _getFormatTime: 
function (remainTime) {
            
var  hour  =  parseInt(remainTime  /   3600 );
            
var  min  =  parseInt(remainTime  /   60 %   60 ;
            
var  second  =  remainTime  %   60 ;
            
var  time  =   this ._stringFormat( ' {0}:{1}:{2} ' ,
                    (hour 
<   10   ?   ' 0 '   +  hour : hour),
                    (min 
<   10   ?   ' 0 '   +  min : min),
                    (second 
<   10   ?   ' 0 '   +  second : second));
            
return  time;
        },
        
/*  从配置中获取指定名称的值  */
        _get: 
function (inst, name) {
            
return  inst.options[name]  !=   null   ?  inst.options[name] : $.counter._defaults[name];
        },
        
/*  添加到目标计数器列表中  */
        _addTarget: 
function (target) {
            
if  ( ! this ._hasTarget(target))  this ._timerTargets.push(target);
        },
        
/*  是否已经包含在目标计数器列表中  */
        _hasTarget: 
function (target) {
            
return  ($.inArray(target,  this ._timerTargets)  >   - 1 );
        },
        
/*  移除指定目标计数器  */
        _removeTarget: 
function (target) {
            
this ._timerTargets  =  $.map( this ._timerTargets,  function (o) {  return  (o  ==  target  ?   null  : o); });
        },
        
// string格式化构造器
        _stringFormat:  function (str) {
            
var  args  =  arguments;
            
return  str.replace( / \{(\d+)\} / g,
                
function (m, i) {
                    
return  args[parseInt(i)  +   1 ];
                });
        }
    });

    
/*  主函数  */
    $.fn.counter 
=   function (options) {
        
var  args  =  Array.prototype.slice.call(arguments,  1 );

        
return   this .each( function () {
            
if  ( typeof  options  ==   ' string ' ) {
                $.counter[
' _ '   +  options  +   ' Counter ' ].apply($.counter, [ this ].concat(args));
            }
            
else  {
                $.counter._attachCounter(
this , options);
            }
        });
    };
    
/*  获取计时器当前时间(总秒数)  */
    $.fn.getTime 
=   function () {
        
var  args  =  Array.prototype.slice.call(arguments,  1 );
        
if  ( this .length  ==   1 ) {
            
return  $.counter[ ' _getTime ' ].apply($.counter, [ this [ 0 ]].concat(args));
        }
        
else   if  ( this .length  >   1 ) {
            
var  array  =  [];
            
this .each( function () {
            
var  time  =  $.counter[ ' _getTime ' ].apply($.counter, [ this ].concat(args));
                
if  (time) {
                    array.push(time);
                }
            });
            
return  array;
        }
        
return   null ;
    };

    $.counter 
=   new  Counter();

})(jQuery);
 

 

附上源代码下载:CounterJSLab.rar

 

最后啦,今天是中秋节,引用一下一位同事的祝福词咯!

忍养安,乐养寿,爱养福,善养运,佛养心,道养行,学养德,诚养誉,礼养谊,动养身,天养地,古养今,问候养情谊! 祝各位博客园的园友中秋快乐!

未命名

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值