`

理想Web倒计时器的设计与实现

 
阅读更多

1 引言
    使用倒计时,可以让用户清楚地了解离特定事件还剩余多少时间,因此在抢答系统、在线考试系统、节日倒计时等应用中都使用到倒计时。在Web应用中,一般使用JavaScript来设计倒计时程序。JavaScript中的setInterval()函数可以作为定时器,每隔一段时间执行指定的事件,但是这种定时器由于运行环境的限制,一旦用户刷新页面或关闭页面再打开,倒计时器又会重新计时,另外,由于JavaScript运行速度较慢,自身运行要占用一定时间,这种倒计时器也很难做到精确计时。
普通倒计时器的缺陷主要表现在以下几个方面:
    ①页面刷新后重新开始倒计时。
    ②面关闭再打开后重新开始倒计时。
    ③无法做到全部客户端同步。
    ④计时不精确,无法对自身进行校正。
    ⑤一旦客户端修改了计算机时钟,倒计时将会失败。
    普通倒计时器之所以存在以上问题,究其原因,在于下面几个方面:
    ⑴定时器设计技术。Web页面中的定时器使用JavaScript进行设计,由于HTTP连接不是一种持续连接,完成用户请求后,连接立即关闭,JavaScript的这种运行环境就决定了它不能记录用户的状态,每当页面刷新或页面关闭再打开后,相应JavaScript的变量会重新进行初始化,表现为倒计时重新计时。
    ⑵ JavaScript的运行速度。JavaScript以解释方法执行,运行速度较编译型语言慢。倒计时器设计时一般会使用到setInterval(function,interval)函数作为定时器,其中,interval为定时器间隔,单位为毫秒,function为定时器每隔interval指定的时间间隔触发的动作。如果function中含有复杂运算,并且由于JavaScript的执行速度较慢,定时器就会被拖慢,例如定义的间隔为1000ms,实际运行的间隔可能为1100ms,这样整个倒计时器的精度就会受到影响。
    ⑶ Web页面的随机请求方式。由于用户的请求是随机的,不可能要求所有用户在同一时间打开页面进行倒计时,所以无法做到所有客户端的倒计时同步。
2 防刷新防关闭自校正的倒计时器设计思路
    单纯使用客户端页面中的JavaScript无法设计出理想的Web倒计时程序,必须把动态脚本技术与客户端的JavaScript结合起来才可以实现符合要求的倒计时器。
    由于客户端显示的时间各不相同,所以不能作为倒计时器的时间参考,否则无法实现所有客户端的倒计时同步,但在B/S系统中,服务器的时间对于每一个客户端来说都是一致的,我们可以把服务器的时间作为倒计时参考时间,实现所有客户端的同步倒计时。
    在动态脚本语言(ASP、PHP、JSP)中可以很方便地取得服务器的当前时间。对于倒计时程序来说,都要指定一个结束时间,可以通过计算出一个服务器当前时间和结束时间之间的时间间隔,这个时间间隔反映了当前服务器时间离倒计时结束时间还有多长时间,无论用户怎样刷新、关闭再打开页面,计算出的这个时间间隔都是与客户端一致的,这样就避免了刷新、关闭再打开后倒计时器重新计时的问题。
    由于JavaScript是一种解释型语言,执行速度较编译型语言慢,每次执行setInterval()函数时每次都会产生一个很小的误差,虽然这个误差很小,但是这些小误差积累起来后却不容忽视,会严重影响倒计时器的精度,进而影响到客户端的同步。虽然各个客户端上的时钟各不相同,但是,我们可以认为所有客户端计算机的时钟步进是一致的,即A计算机时钟产生的1秒时间间隔与B计算机时间产生时间间隔是相同的,所以,我们可以把客户端计算机时钟作为观察倒计时器快慢的参照物,做出setInterval()函数的实际执行时间间隔与本地客户机时钟流逝时间间隔的差值,以这个差值作为反馈,修改setInterval()函数的执行间隔,从而达到自校正的目的,实现倒计时器的精确计时。
    另外,在使用上述自校正方法时,我们要考虑到在定时器执行时,客户修改了本地时钟的情况。一般情况下,我们得到的反馈差值不会太大,这里把1000ms作为阈值,一旦得到的差值大于1000ms,程序会认为客户修改了本地计算机时钟,停止自校正。仍使用原来的间隔时间,这样便解决了这个问题。
3 代码实现
    根据以上的设计思路,使用JSP作为动态脚本语言,实现了一个防刷新、防关闭、自校正、客户端调节时钟不敏感的倒计时器。该倒计时器由两个文件组成,djs.js中为JavaScript倒计时器的主体,实现倒计时功能;djs.jsp中获取服务器时间,调用djs.js中的start()方法开始倒计时,并显示出倒计时结果。
/ *****  djs.jsp  ***** /
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<%
  SimpleDateFormat dfs = new SimpleDateFormat ("yyyy- MM-dd HH:mm:ss");
  java.util.Date begin=new Date();
  java.util.Date end = dfs.parse("2007-08-19 23:37:00");
  long l=end.getTime()-begin.getTime();
  long day=l/(24*60*60*1000);
  long hour=(l/(60*60*1000));
  long hour2=(l/(60*60*1000)-day*24);
  long min=((l/(60*1000))-day*24*60-hour2*60);
  long s=(l/1000-day*24*60*60-hour2*60*60-min*60);
  String interval=hour+":"+min+":"+s;
%>
剩余时间:<span id="clock"><%=interval%></span>
<script type="text/javascript" src="djs.js"></script>
<script language="Javascript">
run();
</script>
 
/****   djs.js  *****/
var counter,startTime,normalelapse = 1000,nextelapse = normalelapse,start =clock.innerText,timer = null;
var alert_time="00:05:00",finish = "00:00:00";
 
function run() {
  counter = 0;
  startTime = new Date().valueOf();  // 初始化开始时间
  // nextelapse是定时时间,初始时为1000毫秒
  // 注意setInterval函数: 时间逝去nextelapse(毫秒)后,onTimer才开始执行
  timer = window.setInterval("onTimer()",nextelapse);
}
 
function stop() { window.clearTimeout(timer);}   // 停止运行
window.onload = function() { }
function onTimer(){  // 倒计时函数
 if(start==alert_time) { alert("还有5分钟结束,请做好结束准备!");}  //剩余5分钟时,提示警告信息
if (start == finish) { //倒计时结束
  window.clearInterval(timer);
  alert("倒计时结束!");
  return;
}
 
var hms = new String(start).split(":");
var s = new Number(hms[2]);
var m = new Number(hms[1]);
var h = new Number(hms[0]);
  s -= 1;
  if (s < 0) {  s = 59;  m -= 1; }
  if (m < 0){  m = 59;  h -= 1; }
var ss = s < 10 ? ("0" + s) : s;
var sm = m < 10 ? ("0" + m) : m;
var sh = h < 10 ? ("0" + h) : h;
start = sh + ":" + sm + ":" + ss ;  //显示倒计时
clock.innerText = start;
window.clearInterval(timer); // 清除上一次的定时器
 
// 自校验系统时间得到时间差,并由此得到下次所启动的新定时器的间隔时间nextelapse
counter++;
var counterSecs = counter *1000;
var elapseSecs = new Date().valueOf() - startTime;  //已经经过的秒数
var diffSecs = counterSecs - elapseSecs;
 
//如果误差在1秒之内,则可以接受
//否则认为客户端用户调整了系统时间,不再进行定时器误差调整,仍使用默认值1000ms
if (Math.abs(diffSecs)<1000)  { 
nextelapse = normalelapse + diffSecs;
      }
 else
        nextelapse=normalelapse;
 
// 启动新的定时器
timer = window.setInterval("onTimer()",nextelapse);
}
4 结束语
    上述代码使用JSP作为动态脚本来实现,如果使用其它动态脚本语言,只需要修改取得当前服务器时间与结束时间的时间间隔片段即可,在此不再赘述。
以上实现的防刷新、防关闭、自校正、客户端修改时钟不敏感的倒计时程序已在笔者设计的在线考试系统中实现,项目实践证明,该倒计时器可以满足以上性能要求,在类似的Web应用中同样有着很好的参考价值。

分享到:
评论
1 楼 on_rain 2011-12-15  
***************
var elapseSecs = new Date().valueOf() - startTime;  //已经经过的秒数
****************
关键一点,你这还是用客户端时间去进行校验的,用户调一下机器时间你的这个倒计时就没用了

相关推荐

Global site tag (gtag.js) - Google Analytics