【项目分析】设计一个计时器的jQuery插件(4)(附赠:中秋节祝福)
时间:2010-09-22 来源:Leepy
背景
目前团队所负责的公务员考试的项目中,需要使用到计数器的前端JS组件,具体应用可以查看页面。
你会发现这里的计时器代码有些问题。
这里使用的是
代码 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
最后啦,今天是中秋节,引用一下一位同事的祝福词咯!
忍养安,乐养寿,爱养福,善养运,佛养心,道养行,学养德,诚养誉,礼养谊,动养身,天养地,古养今,问候养情谊! 祝各位博客园的园友中秋快乐!