我的前端故事----疯狂倒计时(requestAnimationFrame)
很久沒有更新博客了。。。為了雙十一準備了不少活動,終于結束了,有時間靜靜的坐下來總結一下了,在活動中最常用的就是倒計時了,晚上也有很多倒計時的例子了,那么今天帶來的是一個新的方法和思路。
既然要介紹新的方法那就要先說說現在已有的方法的特點了~相信很多剛剛出校門的孩子們還在用setinterval方法來做定時器吧,這種方法可以說是最簡單和最明了的方法了,可是這樣也帶來了很明顯的缺點,那就是setinterval方法在移動端上并不準確,而且及其消耗性能,在配置比較差的機型上還會卡死,所以為了流暢的倒計時,明顯是不能使用這個方法的,所以,接下來我介紹今天的主角,請求動畫幀(requestAnimationFrame)。
在博客園中也有很多介紹requestAnimationFrame的,在這里我就不贅述了,主要是在這個倒計時的時候采用到了這個方法,同時為了消除兼容性的問題,首先還是要在代碼中對requestAnimationFrame進行兼容性的設置的。代碼如下:
(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,比如這樣:
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的時候了,這個時候我采用如下的方式來補償:
var len = ms.toString().length;
while (len < 3) {
ms = "0" + ms;
len++;
}
又到了總結的時候了,首先使用了請求動畫幀來避免了動畫的卡頓,然后使用相對時間差的方式來避免阻塞產生的誤差,當然,上面的代碼還有很多可以優化的地方,這次記錄也是一次不完全的總結吧,接下來我會再介紹一些平時工作用可能會注意到的地方,希望能對剛剛走出校園的同學一些幫助吧~加油~
----jonnyf
總結
以上是生活随笔為你收集整理的我的前端故事----疯狂倒计时(requestAnimationFrame)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 618电商大战(618电商营业额)
- 下一篇: 海思MPP视频处理平台流程简介