倒计时:
手机倒计时会有两个问题:
1、返回到倒计时页面时,倒计时不是最新的
2、锁屏后,再打开,倒计时不是最新的
经校验发现,后退时,Jquery的该方法都会执行,因此可解决问题1.
$(function(){
});
第一方案
会有问题:后退更新,锁屏不更新
$(function(){ $.get('/auction/getTimeCountDown.html',{ auctionIds: timeIds.join(","),idd : Math.random()}, function(data) { afterGetNewSeconds(data); } ); });
启动方法中加入getTimeCountDown请求,每次获取最新倒计时秒数
第二版:
正常,没有问题。
进入时取一次剩余倒计时秒数m
请求前纪录客户端时间a,请求后纪录客户端时间b, (b-a)/2 为网络延时
请求后由当前客户端时间,加上返回的剩余倒计时秒数m,再减去网络延时,得到当前客户端的倒计时结束时间
客户端JS定时器,10秒刷新一次,获取到当前时间与客户端结束时间的秒数差c,刷新倒计时
若定时器倒计时的秒数(1秒刷一次,10秒后的秒数)与 c 误差在1秒内,不刷新定时器,否则清除老定时器,生成新定时器
具体代码如下:
HTML:
倒计时:<i class="auctTime" id="<@getStringValue cdo=cdoAuction col='nAuctionId' />"></i>
timer.js:
var finishArray = []; var timerArray = []; //处理网络延时时间 var startTime ; var endTime ; //保存歌倒计时定时器,清除定时器时使用 var map = new HashMap(); //保存本地倒计时中的最新秒数,与10秒刷新获取的本地客户端结束时间倒计时比较 var map2 = new HashMap(); var obj = new Object(); $(function(){ //获取需倒计时的兑换产品Id var timeIds = []; $(".auctTime").each(function(){ var id = $(this).attr("id"); timeIds[timeIds.length] = id; }) if(timeIds.length == 0) return; startTime = new Date(); //获取最新时间 $.get('/auction/getTimeCountDown.html',{ auctionIds: timeIds.join(","),idd : Math.random()}, function(data) { afterGetNewSeconds(data); } ); window.setInterval(refreshCountDown,10000); }); function timer(intDiff,id){ var intervalname = "interval"+id; var timerVar = window.setInterval(function(){ var day=0, hour=0, minute=0, second=0;//时间默认值 if(intDiff > 0){ day = Math.floor(intDiff / (60 * 60 * 24)); hour = Math.floor(intDiff / (60 * 60)) - (day * 24); minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60); second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60); } hour = day * 24 + hour; if (hour <= 9) hour = '0' + hour; if (minute <= 9) minute = '0' + minute; if (second <= 9) second = '0' + second; var str = ""; str += hour + ":"; str += minute + ":"; str += second; intDiff--; console.log("======================="); map2.put(intervalname,intDiff); $("#" + id).html(str); if(intDiff <= 0){ if(typeof isdetail != "undefined" && isdetail == "1"){ //刷新当前页面 location.reload(); }else{ $("#bidding_"+id).remove(); $(".cd_"+id).hide(); $("#btn_"+id).text("竞拍结束"); clearInterval(timerVar); } } }, 1000); map.put(intervalname,timerVar); timerArray[timerArray.length] = timerVar; } //获取客户端当前时间与 客户端结束时间作比较,刷新倒计时 function refreshCountDown(){ if(finishArray.length >= 0){ for(var i in finishArray){ refreshPerCountDown(finishArray[i]); } } } function afterGetNewSeconds(data){ endTime = new Date(); var interval = endTime.getTime() - startTime.getTime(); data = eval('(' + data + ')'); if(data.code == 0){ var result = data.result; if(typeof result == "undefined" || result.length == 0) return; for(var i in result){ var exId = result[i].auctionId; var newSecond = result[i].seconds; var newMiSeconds; if(interval > 0){ //newSecond = newSecond - (interval/1000); newMiSeconds = (newSecond * 1000) - (interval/2); } //计算客户端结束时间 var finishTime = new Date().getTime() + newMiSeconds; var finishTimeStr = exId+"_"+finishTime; finishArray[finishArray.length] = finishTimeStr; refreshPerCountDown(finishTimeStr); } } } var exId,finishTime,seconds; //刷新单个 已保存客户端结束时间的倒计时 function refreshPerCountDown(finishTimeStr){ //clearInterval(intervalVar); var curTime = new Date().getTime(); exId = finishTimeStr.split("_")[0]; finishTime = finishTimeStr.split("_")[1]; seconds = (finishTime - curTime)/1000; refreshTimer(exId,seconds); } //刷新最新倒计时 function refreshTimer(exId,newSecond){ var intervalname = "interval"+exId; var sed = map2.get(intervalname); var diff = parseFloat(sed) - parseFloat(newSecond); if(sed == null || diff >=1){ clearInterval(map.get(intervalname)); timer(newSecond,exId); } }
后台:
/** * 获取最新倒计时 */ public void getTimeCountDown(){ CDO seconds = new CDO(); String auctionIds = request.getParameter("auctionIds"); if(StringUtils.isBlank(auctionIds)){ seconds.setStringValue("code", "-1"); ajaxForAction(response, seconds.toJSON()); return; } cdoRequest.setStringValue(ITransService.SERVICENAME_KEY, "AuctionService"); cdoRequest.setStringValue(ITransService.TRANSNAME_KEY, "getTimeCountDown"); cdoRequest.setStringValue("auctionIds", auctionIds); Return ret = getServiceBus().handleTrans(cdoRequest, cdoResponse); if(ret.getCode() == 0){ if(cdoResponse.exists("cdosExchange")){ CDO[] cdos = cdoResponse.getCDOArrayValue("cdosExchange"); CDO[] cdosExchange = new CDO[cdos.length]; int i = 0; for(CDO cdo : cdos){ String dataString = cdo.getStringValue("ex"); //if(StringUtils.isBlank(dataString)) continue; String exId = dataString.substring(0,dataString.indexOf("-")); String strTime = dataString.substring(dataString.indexOf("-")+1); long nSecond = DateUtil.getSecond(strTime); CDO exchangeCdo = new CDO(); exchangeCdo.setStringValue("auctionId", exId); exchangeCdo.setLongValue("seconds", nSecond); cdosExchange[i] = exchangeCdo; i++; } if(cdosExchange.length >0){ seconds.setCDOArrayValue("result", cdosExchange); } } } seconds.setStringValue("code", "0"); ajaxForAction(response, seconds.toJSON()); }
SQL:
select CONCAT_WS("-",a.nAuctionId,a.dtEndTime) ex from tbauction a where a.nAuctionId in (
。。。
最新版优化:
最后1分钟或几分钟内,出价后,时间就变为1分钟,直到无人出价为止。
参考了西部数码的竞拍,发现以前考虑的太复杂了,哈哈
西部的JS见附件。
注意的点:
出价成功或失败时,提示使用的不是alert弹窗,而是用的jbox。
alert弹窗出现时,若一直不点,则定时器也会暂停,点击后才继续接着执行,这样如果各客户端点击快慢不同,就会导致各客户端最后的倒计时时间不同。而Jbox则不会阻塞JS,使用jbox.tip也更人性化。
参考:
当有非常多的setTimeout和setInterval时,哪种用法效率高?
======================================================================
1、setInterval计时器不准的问题(setInterval回调堆积): 在js中如果打算使用setInterval进行倒数,计时等功能,往往是不准确的,因为setInterval的回调函数并不是到时后立即执行,而是等系统计算资源空闲下来后才会执行.而下一次触发时间则是在setInterval回调函数执行完毕之后才开始计时,所以如果setInterval内执行的计算过于耗时,或者有其他耗时任务在执行,setInterval的计时会越来越不准,延迟很厉害.
2、setTimeout基于JavaScript运行时内部的事件队列
jQuery源码里很多地方都用到了setTimeout(callback)可以去看看。打个比方:
function read_data(){
read_file();
write_log();
//setTimeout(write_log,0);
}
read_data();
show_data();
假设write_log()花费1秒,那么read_data()得多等1秒才能返回,然后show_data()才能把数据展示给用户。但是用户根本不关心你的日志,让用户为此多等1秒其实毫无意义,这种场景就可以用setTimeout(callback,0)来调用write_log,让read_data()立刻返回并将数据展示给用户。node里可以用nextTick,会更快一些。
伪异步:
【斩草除根】重新认识所谓的“异步”
我们知道,js是单线程执行的。事实上setTimeout和setInterval只是简简单单地通过插入代码到代码队列来实现代码的延迟执行(或者说异步执行)。但是事实上所谓的异步只是一个假象——它同样运行在一个线程上!
那么问题就来了,要是在插入点前的代码执行时间超过了传入setTimeout或setInterval的设定时间会怎样呢?让我们来看看这段代码:
function fn() { setTimeout(function(){alert('can you see me?');},1000); while(true) {} }
你觉得这段代码的执行结果是什么呢?答案是,alert永远不会出现。
这是为什么呢?因为,while这段代码没有执行完,插入在后面的代码便永远不会执行。
综上所述,其实JS终归是单线程产物。无论如何“异步”都不可能突破单线程这个障碍。所以许多的“异步调用”(包括Ajax)事实上也只是“伪异步”而已。只要理解了这么一个概念,也许理解setTimeout和setInterval也就不难了。
=============================================================================
概要代码:
var leftsecond=0; var timerobj = null ; $(function(){ //获取需倒计时的兑换产品Id var timeIds = []; $(".auctTime").each(function(){ var id = $(this).attr("id"); timeIds[timeIds.length] = id; }) if(timeIds.length == 0) return; startTime = new Date(); //获取最新时间 $.get('/auction/getTimeCountDown.html',{ auctionIds: timeIds.join(","),idd : Math.random()}, function(data) { afterGetNewSeconds(data); } ); if(state == "2"){ daojishi(); } //window.setInterval(refreshCountDown,10000); }); /////////////////////////////////////////////////////////////// function timespan(intDiff){ console.log("intDiff::::::" + intDiff); //if(intDiff <= 0) return; var day=0, hour=0, minute=0, second=0;//时间默认值 if(intDiff > 0){ day = Math.floor(intDiff / (60 * 60 * 24)); hour = Math.floor(intDiff / (60 * 60)) - (day * 24); minute = Math.floor(intDiff / 60) - (day * 24 * 60) - (hour * 60); second = Math.floor(intDiff) - (day * 24 * 60 * 60) - (hour * 60 * 60) - (minute * 60); } hour = day * 24 + hour; if (hour <= 9) hour = '0' + hour; if (minute <= 9) minute = '0' + minute; if (second <= 9) second = '0' + second; var str = ""; str += hour + ":"; str += minute + ":"; str += second; console.log("str:" + str); return str; } function daojishi(){ var time = timespan(leftsecond--); $("#" + auctionId).text( time ); clearTimeout(timerobj); timerobj = setTimeout("daojishi()",1000) } function afterGetNewSeconds(data){ endTime = new Date(); var interval = endTime.getTime() - startTime.getTime(); data = eval('(' + data + ')'); //console.log("data================="); if(data.code == 0){ //console.log("data2================="); var result = data.result; if(typeof result == "undefined" || result.length == 0) return; //console.log("data3================="); for(var i in result){ var exId = result[i].auctionId; var newSecond = result[i].seconds; if(parseInt(auctionId) - parseInt(exId) == 0){ if( Math.abs(newSecond-leftsecond)>=3 ){ leftsecond = parseInt(newSecond) - 1; //正负10的情况下才去改变这个数字// 减1是因为网页上timer要延迟一秒 //console.log("leftsecond=================" + leftsecond); } } } } } function refreshDetailTime(autId,seconds){ console.log("seconds :" + seconds + ",leftsecond:" + leftsecond); if( Math.abs(seconds-leftsecond)>=3 ){ console.log("refreshDetailTime33====" + leftsecond); leftsecond = parseInt(seconds) - 1; //正负10的情况下才去改变这个数字// 减1是因为网页上timer要延迟一秒 console.log("refreshDetailTime44 leftsecond====" + leftsecond); } }
refreshDetailTime 方法为刷出价时,同时刷回来最新的剩余倒计时秒数。
相关推荐
在这个抽奖程序中,CSS3可能被用来设计美观的界面,通过使用选择器、伪类和过渡(transition)、动画(animation)等特性实现动态效果。例如,当用户点击抽奖按钮时,转盘可能会有平滑的旋转动画;中奖信息显示时,...
鉴于描述中并未明确提及,但一个现代化的表白网可能会采用响应式设计,确保在不同设备(如手机、平板电脑、桌面电脑)上都能良好展示。这通常通过媒体查询(Media Queries)和流式布局(Fluid Grids)来实现,确保...
1. **CSS选择器**:在"乌木项目"中,开发者可能会使用各种高级选择器,如类选择器、ID选择器、属性选择器、伪类和伪元素,来精准地定位和控制网页元素的样式。 2. **CSS布局技术**:可能包含了Flexbox或Grid布局,...