背景
目前团队所负责的公务员考试的项目中,需要使用到计数器的前端JS组件,具体应用可以查看页面。
你会发现这里的计时器代码有些问题。
这里使用的是
$ 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 >
< 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. 具体计时的是在何时触发的呢?
/* 共享所有的timer */
_timer: setInterval( function () { $.counter._updateTargets(); }, 900 ),
/* 当前激活的timer列表 */
_timerTargets: [],
…
});
这里每隔900毫秒会执行一次事件(可以根据您的情况来设置)
/* 获取当前计数器的时间秒数 */
_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参数来实现暂停和继续的功能:
$( 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
最后啦,今天是中秋节,引用一下一位同事的祝福词咯!
忍养安,乐养寿,爱养福,善养运,佛养心,道养行,学养德,诚养誉,礼养谊,动养身,天养地,古养今,问候养情谊! 祝各位博客园的园友中秋快乐!