最近在多个项目都遇到了一个倒计时的功能,即当前时间距离某个时间点的剩余时间,由于每次都要拷贝大段的代码,所以为了减少自己的下一次开发的工作量,特地将倒计时的功能封装起来,在这里描述一下插件开发的过程。
jquery插件常见的开发方式有三种,第一种extend,第二种通过$.fn添加方法,第三种就是对象的开发方式,这种方式常见于jqueryui,比方说easyui,ligerui等等。在这里使用了第三种,模仿easyui的实现。花了2,3天晚上终于实现了一个可执行的版本
插件开发默认架构就是如下一段代码,
(function($) {
$.fn.counttime = function(options, params) {
return this.each(function() {
var opts = $.extend({},
$.fn.counttime.defaults, options);
if (opts.endString) {
var dString = opts.getCountString.call(this, opts.endString);
$(this).html(dString);
} else {
opts.start = timeExchange(options.startTime);
opts.end = timeExchange(options.endTime);
$.data(this, "counttime", opts);
jq = this;
showTime();
}
});
};
var jq;
var showTime = function() {
var opts = $.data(jq, "counttime");
opts.start = opts.start + 1000;
var distance = opts.end - opts.start;
if (distance < 0) {
$(jq).html("");
} else {
var countDownloadString = opts.getCountString.call(this, getCountDownString(distance));
$(jq).html(countDownloadString);
}
$.data(jq, "counttime", opts);
timeID = setTimeout(showTime, 1000);
}
// 方法
$.fn.counttime.methods = {
options: function(jq) {
return $.data(jq[0], "mxtabs").options;
},
};
// 默认值
$.fn.counttime.defaults = {
startTime: "",
endTime: "",
start: 0,
end: 0,
getCountString: function(downString) {
return downString;
},
};
/**
* 获取字符串
*/
function getCountDownString(distance) {
if (distance < 0) return "0天 00:00:00";
var day = Math.floor(distance / (3600000 * 24));
distance -= day * 3600000 * 24;
var hour = Math.floor(distance / 3600000);
distance -= hour * 3600000;
var minute = Math.floor(distance / 60000);
distance -= minute * 60000;
var second = Math.floor(distance / 1000);
if (day >= 1) day = day + "天 ";
else day = "";
if (hour < 10) hour = "0" + hour;
if (minute < 10) minute = "0" + minute;
if (second < 10) second = "0" + second;
return day + hour + ":" + minute + ":" + second;
}
/**
* 解析时间字符串
*/
function timeExchange(datastr) {
var date = new Date();
var perfex = datastr.split(" ")[0];
var surfex = datastr.split(" ")[1];
if (perfex) {
var prefexs = perfex.split("-");
var y = 0;
if (prefexs.length >= 1) {
y = prefexs[0] / 1;
}
var m = 0;
if (prefexs.length >= 2) {
// 因为月是从0开始的,所以减一
m = prefexs[1] / 1 - 1;
}
var d = 0;
if (prefexs.length >= 3) {
d = prefexs[2] / 1;
}
date.setFullYear(y, m, d);
}
if (surfex) {
var surfexs = surfex.split(":");
var h = 0;
if (surfexs.length >= 1) {
h = surfexs[0] / 1;
}
var m = 0;
if (surfexs.length >= 2) {
// 因为月是从0开始的,所以减一
m = surfexs[1] / 1 - 1;
}
var s = 0;
if (surfexs.length >= 3) {
s = surfexs[2] / 1;
}
date.setHours(h, m, s, 0);
}
return date.getTime();
}
})(jQuery);
初始的想法是使用settimeout,代码如下,而后发现settimeout的作用域问题,使用字符串无法调用插件内部的方法,而后使用方法,则发现会死循环,为了解决这个问题找了好几个jquery倒计时的插件,其中jquery.redcountdown.js fliptimer.js是使用使用setInterval来实现的,在这个过程还了解到了javascript的反混淆,没有浏览器厂商的支持,javascript永远无法反混淆,写的代码没有足够的保护。
最终改写的代码如下:
(function($) {
$.fn.counttime = function(options, params) {
return this.each(function() {
var opts = $.extend({},
$.fn.counttime.defaults, options);
if (opts.endString) {
var dString = opts.getCountString.call(this, opts.endString);
$(this).html(dString);
} else {
opts.start = timeExchange(options.startTime);
opts.end = timeExchange(options.endTime);
opts.interID = setInterval(showTime, 1000);
$.data(this, "counttime", opts);
jq = this;
showTime();
}
});
};
var jq;
var showTime = function() {
var opts = $.data(jq, "counttime");
opts.start = opts.start + 1000;
var distance = opts.end - opts.start;
if (distance < 0) {
$(jq).html("");
if (opts.interID) {
clearInterval(opts.interID);
}
} else {
var countDownloadString = opts.getCountString.call(this, getCountDownString(distance));
$(jq).html(countDownloadString);
}
$.data(jq, "counttime", opts);
// timeID=setTimeout(showTime,1000);
}
// 方法
$.fn.counttime.methods = {
options: function(jq) {
return $.data(jq[0], "mxtabs").options;
},
};
// 默认值
$.fn.counttime.defaults = {
startTime: "",
endTime: "",
start: 0,
end: 0,
getCountString: function(downString) {
return downString;
},
};
/**
* 获取字符串
*/
function getCountDownString(distance) {
if (distance < 0) return "0天 00:00:00";
var day = Math.floor(distance / (3600000 * 24));
distance -= day * 3600000 * 24;
var hour = Math.floor(distance / 3600000);
distance -= hour * 3600000;
var minute = Math.floor(distance / 60000);
distance -= minute * 60000;
var second = Math.floor(distance / 1000);
if (day >= 1) day = day + "天 ";
else day = "";
if (hour < 10) hour = "0" + hour;
if (minute < 10) minute = "0" + minute;
if (second < 10) second = "0" + second;
return day + hour + ":" + minute + ":" + second;
}
/**
* 解析时间字符串
*/
function timeExchange(datastr) {
var date = new Date();
var perfex = datastr.split(" ")[0];
var surfex = datastr.split(" ")[1];
if (perfex) {
var prefexs = perfex.split("-");
var y = 0;
if (prefexs.length >= 1) {
y = prefexs[0] / 1;
}
var m = 0;
if (prefexs.length >= 2) {
// 因为月是从0开始的,所以减一
m = prefexs[1] / 1 - 1;
}
var d = 0;
if (prefexs.length >= 3) {
d = prefexs[2] / 1;
}
date.setFullYear(y, m, d);
}
if (surfex) {
var surfexs = surfex.split(":");
var h = 0;
if (surfexs.length >= 1) {
h = surfexs[0] / 1;
}
var m = 0;
if (surfexs.length >= 2) {
// 因为月是从0开始的,所以减一
m = surfexs[1] / 1 - 1;
}
var s = 0;
if (surfexs.length >= 3) {
s = surfexs[2] / 1;
}
date.setHours(h, m, s, 0);
}
return date.getTime();
}
})(jQuery);
调用的实现如下
$("#countTime").counttime({
startTime: '2015-10-2 11:20:00',
endTime: '2015-10-2 11:20:10'
});
在这个过程认识到了一些jquery插件的写法,变量的定义,内部方法的互相调用,外部如何调用内部方法,以及缓存$.data()的思想,以前都是存储在dom中然后再读取的,以及$.extend()对数据扩展等功能。