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

背景

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

1

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

这里使用的是

代码
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. 首先先来看下最后的调用方法是什么样的:

代码
<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中的核心代码:

代码
/* 主函数 */ 
$.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)。

接着看:

代码
/* 绑定计数器 */ 
_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中去维护。

代码
/* 重新显示计数器 */ 
_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毫秒会执行一次事件(可以根据您的情况来设置)

代码
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方法来获取剩余时间:

代码
/* 获取计时器当前时间(总秒数) */ 
$.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'); 

);

其他的请看完整代码:

代码
/*
* 计数器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

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

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

未命名

原文地址:https://www.cnblogs.com/liping13599168/p/1833120.html