倒计时—精确与服务器时间保持一致

一、背景

        在项目中,需要使用倒计时来提醒用户做出相应的操作,并且对倒计时的精准性有比较高的要求。一开始是页面直接获取服务器时间,在页面设置定时器【setInterval(function{…}, 1000);】,但发现各个页面从一开始加载出的时间就不一致,并且用户做出相应的操作时会导致定时器卡住,致使误差更大。

二、原因分析

     致使这种情况的大致原因如下:

  1. 网络通讯需要耗费一定的时间;(暂时没想到办法计算
  2. 页面加载需要一定时间,并且不同浏览器的加载时间也不一样;
  3. JS是单线程运行的,当有其他操作就会致使定时器卡住,等其他操作结束之后再执行,造成误差。

倒计时误差模型

 注:此模型中未考虑由于用户的操作引起定时器阻塞导致的时间误差。

 二、JS实现较为精准的倒计时

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>倒计时</title>
        <script>
            var startTime = new Date().getTime();    // 此时时间(用于计算线程占有,以及渲染引起的时间误差)
            var i = 0;
            while(i < 80000){    // 模拟页面加载(占用线程)
                ++ i;
            }
        </script>
        <style>
            .outerDiv {
                width: 880px;
                margin: 50px auto;
            }
            .countDown {
                text-align: center; 
                padding-top: 10%; 
                padding-bottom: 5%; 
                line-height: 80px; 
                font-size: 46px; 
                color: #333;
            }
            .countDown i { 
                color: #fff; 
                display: inline-block; 
                border-radius: 10px; 
                width: 68px;
                height: 80px;
                background: #ff9f09; 
                margin: 0 6px;
                padding-right: 10px;
            }
        </style>
    </head>
    <body>
        <div class="outerDiv">
            <div id="timeStrDiv" class="countDown">
			
            </div>
        </div>
    </body>
    <script>
        window.onload = function(){
            countDown();
        };
		
        var timer = null;
        var showStr = "";
        var showDiv = document.getElementById("timeStrDiv");
        function countDown(times){
            if (times == null || times == '') {
                times = 24321;    // 从服务器获取得倒计时(单位为毫秒)
                times -= (new Date().getTime() - startTime);    // 获取倒计时开始时间(减去页面加载时间)
            }
            if (timer != null) {
                clearTimeout(timer);
            }
			
            var interval = 1000;    // 设定倒计时标准间隔差
            startTime = new Date().getTime();    // 此时时间(用于计算线程占有,以及渲染引起的时间误差)
            var count = 0;    // 标记执行次数
			
            // 判断是否需要倒计时
            if (Math.floor(times / interval) > 0) {
                timer = setTimeout(countDownStart, interval);
            } else {
                showStr = "倒计时:<i>00</i>天<i>00</i>小时<i>00</i>分钟<i>00</i>秒";
                showDiv.innerHTML = showStr;
            }
			
            /** 真正的倒计时  */
            function countDownStart(){
                count++;
				
                var j = 0;
                while(j < (count * 8000000)){    // 模拟逻辑代码(占用线程)
                    ++ j;
                }
				
                showStr = formatTime("倒计时:<i>DD</i>天<i>HH</i>小时<i>MM</i>分钟<i>ss</i>秒", times);
                showDiv.innerHTML = showStr;
				
                var offset = new Date().getTime() - (startTime + count * interval);   // 计算误差
                if (offset > interval) {    // 若误差超过间隔时间,则立即校正(会出现跳秒的情况)
                    times -= Math.floor(offset / interval) * interval;
                    count += Math.floor(offset / interval);
                    offset = offset % interval;
                }
                var nextTime = interval - offset;    // 标准时间间隔减去此次的误差,为下一次执行的时间
				
                if (Math.floor(times / interval) <= 0) {
                    clearTimeout(timer);
                    showStr = "倒计时:<i>00</i>天<i>00</i>小时<i>00</i>分钟<i>00</i>秒";
                    showDiv.innerHTML = showStr;
                } else {
                    times -= interval;
                    timer = setTimeout(countDownStart, nextTime);
                }
				
                console.log("执行次数:" + count + ", 误差:" + offset + "ms, 下一次执行:" + nextTime + "ms之后, 所剩时间:" + times + "ms");
            }
        }
		
        /** 格式化时间形式 */
        function formatTime(fmt, time){
            fmt = fmt.toUpperCase();
            var day = formatTimeField(Math.floor(time / (1000 * 60 * 60 * 24)));
            var hour = formatTimeField(Math.floor(time / (1000 * 60 * 60)) - (day * 24));
            var minute = formatTimeField(Math.floor(time / (1000 * 60)) - (day * 24 * 60) - (hour * 60));
            var second = formatTimeField(Math.floor(time / 1000) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60));
            var timeField = {
                "D+": day,
                "H+": hour,
                "M+": minute,
                "S+": second
            };
            for(var field in timeField){
                if(new RegExp("(" + field + ")").test(fmt)){
                    fmt = fmt.replace(RegExp.$1, (RegExp.$1.length===1) ? (timeField[field]) : (("00" + timeField[field]).substr(("" + timeField[field]).length )));
                }
            }
            return fmt;
        }
			
        /** 格式化时间分量 */
        function formatTimeField(number){
            if (number <= 9) {
                number = "0" + number;
            }
            return number;
        }
    </script>
</html>

 

实现这三种功能可以使用JavaScript编写脚本。 1. 获取当前日期时间 可以使用JavaScript中的Date对象来获取当前的日期和时间,代码如下: ```javascript var now = new Date(); // 创建Date对象 var year = now.getFullYear(); // 获取年份 var month = now.getMonth() + 1; // 获取月份,注意月份从0开始,需要加1 var day = now.getDate(); // 获取日期 var hour = now.getHours(); // 获取小时 var minute = now.getMinutes(); // 获取分钟 var second = now.getSeconds(); // 获取秒钟 ``` 2. 显示日期时间 可以将获取到的日期时间信息显示在网页上,代码如下: ```html <p id="datetime"></p> <script> var now = new Date(); var year = now.getFullYear(); var month = now.getMonth() + 1; var day = now.getDate(); var hour = now.getHours(); var minute = now.getMinutes(); var second = now.getSeconds(); var datetime = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; document.getElementById("datetime").innerHTML = datetime; </script> ``` 3. 倒计时 要实现倒计时功能,需要先确定倒计时的目标时间,然后通过计算当前时间与目标时间的差值,得到剩余的时间,再将剩余的时间显示在网页上。 代码如下: ```html <p id="countdown"></p> <script> var targetTime = new Date("2021/12/31 23:59:59"); // 目标时间 var now = new Date(); // 当前时间 var remainingTime = targetTime.getTime() - now.getTime(); // 剩余时间,单位毫秒 var days = Math.floor(remainingTime / (1000 * 60 * 60 * 24)); // 剩余天数 var hours = Math.floor((remainingTime % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); // 剩余小时数 var minutes = Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60)); // 剩余分钟数 var seconds = Math.floor((remainingTime % (1000 * 60)) / 1000); // 剩余秒数 var countdown = days + "天" + hours + "小时" + minutes + "分钟" + seconds + "秒"; document.getElementById("countdown").innerHTML = countdown; </script> ``` 注意,以上代码中的目标时间需要根据实际情况修改。另外,倒计时可能会受到客户端时间服务器时间不一致的影响,建议在服务器端计算倒计时
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值