好久沒有更新博客了。。。爲了雙十一準備了很多活動,終於結束了,有時間靜靜的坐下來總結一下了,在活動中最經常使用的就是倒計時了,晚上也有不少倒計時的例子了,那麼今天帶來的是一個新的方法和思路。android
既然要介紹新的方法那就要先說說如今已有的方法的特色了~相信不少剛剛出校門的孩子們還在用setinterval方法來作定時器吧,這種方法能夠說是最簡單和最明瞭的方法了,但是這樣也帶來了很明顯的缺點,那就是setinterval方法在移動端上並不許確,並且及其消耗性能,在配置比較差的機型上還會卡死,因此爲了流暢的倒計時,明顯是不能使用這個方法的,因此,接下來我介紹今天的主角,請求動畫幀(requestAnimationFrame)。ios
在博客園中也有不少介紹requestAnimationFrame的,在這裏我就不贅述了,主要是在這個倒計時的時候採用到了這個方法,同時爲了消除兼容性的問題,首先仍是要在代碼中對requestAnimationFrame進行兼容性的設置的。代碼以下:web
(function(window) { "use strict"; var lastTime = 0; window.requestAnimationFrame = window.requestAnimationFrame || window.webkitrequestAnimationFrame || function(callback) { var currTime = Date.now(), timeToCall = Math.max(0, 16 - (currTime - lastTime)), id = setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; })(window);
在設置已上代碼以後即可以直接使用了,那麼接下來就上倒計時的代碼,而後我再一一介紹:服務器
/** * 小時級倒計時動畫 * @param {String} time [服務器時間戳] * @param {String} time [倒計時截至時間] */ function _timeAnimation(time, timesNum) { var times = (timesNum - time), // 目標時間和服務器時間的差值 timeTemp, // 臨時時間 remain_sec = 0, // 秒 remain_minute = 0, // 分鐘 remain_hour = 0, // 小時 timetag = Date.now(), // 上一幀的時間 hour = 0, // 最終顯示小時 min = 0, // 最終顯示分鐘 sec = 0, // 最終顯示秒 doms = document.getElementById('times'); // 須要渲染的DOM元素 timeTemp = parseInt(times / 1000); // 秒數 remain_sec = timeTemp % 60; timeTemp = parseInt(timeTemp / 60); // 分數 remain_minute = timeTemp % 60; timeTemp = parseInt(timeTemp / 60); // 小時數 remain_hour = timeTemp % 24; timeTemp = parseInt(timeTemp / 24); function begin() { if ((Date.now() - timetag) >= 1000) { times = timesNum - Date.now(); timeTemp = parseInt(times / 1000); // 秒數 remain_sec = timeTemp % 60; timeTemp = parseInt(timeTemp / 60); // 分數 remain_minute = timeTemp % 60; timeTemp = parseInt(timeTemp / 60); // 小時數 remain_hour = timeTemp % 24; timeTemp = parseInt(timeTemp / 24); // 當時間結束後倒計時中止 if ((remain_minute <= 0) && (remain_sec <= 0) && (remain_hour <= 0)) { remain_minute = remain_sec = remain_hour = 0; return; } timetag = Date.now(); } // 如下部分作爲時間顯示時補零 if (remain_hour < 10) { hour = '0' + remain_hour; } else { hour = remain_hour; } if (remain_minute < 10) { min = '0' + remain_minute; } else { min = remain_minute; } if (remain_sec < 10) { sec = '0' + remain_sec; } else { sec = remain_sec; } doms.innerHTML = hour + ':' + min + ':' + sec; window.requestAnimationFrame(begin); } window.requestAnimationFrame(begin); }
如今代碼貼上來了,那接下來我就介紹一下思路,正常來講,不少人都會在初始化的時候計算出三個時間來,而後在分別在倒計時的時候減1,好比這樣:dom
if ((Date.now() - timetag) >= 1000) { if (remain_sec > 0) { remain_sec--; } else if (remain_minute > 0) { remain_minute--; remain_sec = 59; } else if (remain_hour > 0) { remain_hour--; remain_minute = 60; } else { remain_hour = remain_minute = remain_sec = 0; return; } timetag = Date.now(); }
這樣作的結果就是產生偏差,那麼有同窗就要問了,這樣會在什麼狀況下產生偏差呢?性能
那就是當用戶觸發了alert窗口的時候,js代碼就會被阻塞,這個時候這樣的倒計時就會中止,那麼當用戶再回來的時候就會產生必定的偏差,那有人問了,個人活動沒有alert呢?會不會也產生偏差呢?或者說我不使用alert,而是用遮罩來模仿alert呢?這樣會不會就能避免了呢?其實這樣的話在android設備上還說的過去,可是在ios設備上面的話就會出問題,由於系統的特性,當用戶點擊屏幕以後,就會和alert同樣阻塞代碼的執行,因此這個時候若是用戶不當心點了屏幕沒有鬆手,那偏差就會不斷的產生了。優化
因此就不能使用相似上面的倒計時方法了,爲了不這樣的偏差產生,因此應該是用當前時間減去上一幀的時間,而後轉成秒去減,但這樣其實也是有問題的,那就是若是用戶阻塞的好久,十幾分鍾,幾個小時的話就很差處理了,因此一個更加偷懶的辦法就是用目標時間來減去當前時間,而後在去換算成小時,分鐘和秒,就如同我代碼上面的那樣,而服務器的時間是否是就沒有用了呢?並非,服務器的時間做爲初始化的校驗時間是十分有必要的,這樣能夠避免用戶修改了本地時區的時候提早開始倒計時,因此須要服務器的時間來進行矯正,若是用戶的時間比服務器的時間早,或者晚,那麼就不進行倒計時了。動畫
接下來就是喜聞樂見的補0操做了,由於上面的代碼是最終精確到秒的,因此補0仍是很簡單的,當你的精確度到達毫秒的時候就須要連續補2個0的時候了,這個時候我採用以下的方式來補償:spa
var len = ms.toString().length; while (len < 3) { ms = "0" + ms; len++; }
又到了總結的時候了,首先使用了請求動畫幀來避免了動畫的卡頓,而後使用相對時間差的方式來避免阻塞產生的偏差,固然,上面的代碼還有不少能夠優化的地方,此次記錄也是一次不徹底的總結吧,接下來我會再介紹一些平時工做用可能會注意到的地方,但願能對剛剛走出校園的同窗一些幫助吧~加油~code
----jonnyf