日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
基于RequestAnimationFrame實(shí)現(xiàn)高精度毫秒級(jí)正向計(jì)時(shí)器

背景

最近做了一個(gè)周末嘉年華的活動(dòng)【免費(fèi)領(lǐng)取「王者榮耀千元賬號(hào)」】,效果圖如下。玩法也很簡(jiǎn)單:點(diǎn)擊開(kāi)始,計(jì)時(shí)器開(kāi)始計(jì)時(shí),點(diǎn)擊停止,點(diǎn)擊開(kāi)始按鈕后會(huì)變成停止,當(dāng)計(jì)時(shí)結(jié)束時(shí),秒表顯示時(shí)間為 10:00 時(shí),即可獲取 「價(jià)值千元的王者榮耀賬號(hào)」!

網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信平臺(tái)小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了呼倫貝爾免費(fèi)建站歡迎大家使用!

編組

點(diǎn)我體驗(yàn) ?。?!

若遇到活動(dòng)未開(kāi)始或者活動(dòng)結(jié)束,可以前往轉(zhuǎn)轉(zhuǎn)app搜索【游戲】即可參與更多活動(dòng),各種福利拿到手軟!

需求分析

從圖上可以看出來(lái),核心就是一個(gè)正向計(jì)時(shí)器。通過(guò)js實(shí)現(xiàn)一個(gè)普通的正向計(jì)時(shí)器很簡(jiǎn)單,大多數(shù)想到都是使用setInterval來(lái)實(shí)現(xiàn)。那么還有沒(méi)有其他的實(shí)現(xiàn)方式呢?又怎么去實(shí)現(xiàn)一個(gè)高精度的毫秒級(jí)正向計(jì)時(shí)器呢?

最近看了vant4的倒計(jì)時(shí)組件的源碼,發(fā)現(xiàn)其并沒(méi)有使用setInterval, 而是封裝了requestAnimationFrame 和利用 Date.now()來(lái)處理毫秒級(jí)渲染和倒計(jì)時(shí)實(shí)現(xiàn)。那么能不能通過(guò)requestAnimationFrame來(lái)實(shí)現(xiàn)一個(gè)正向計(jì)時(shí)器呢?

先看看效果圖,接下來(lái)將會(huì)一步步去實(shí)現(xiàn):

體驗(yàn)地址: https://suyxh.github.io/timer-demo/

setInterval版

首先呢,來(lái)看看使用setInterval是如何實(shí)現(xiàn)的。在網(wǎng)上看了很多文章,大多都是使用的 setInterval 去實(shí)現(xiàn),大致效果如下:

setinterval

從效果圖上我們可以發(fā)現(xiàn),最后一位始終為0,甚至還是有些小bug,很明顯不是我們想要的。具體代碼如下:








Document
















requestAnimationFrame版

上文中提到vant的CutDown組件,主要就是利用 Date.now() 會(huì)自己走的原理,結(jié)合 requestAnimationFrame 去做時(shí)間計(jì)算;那么正向計(jì)時(shí)器則是利用了 requestAnimationFrame 回調(diào)函數(shù)的參數(shù)去做時(shí)間計(jì)算,從而實(shí)現(xiàn)毫秒級(jí)的計(jì)時(shí)器。

「window.requestAnimationFrame()」 告訴瀏覽器——你希望執(zhí)行一個(gè)動(dòng)畫(huà),并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動(dòng)畫(huà)。該方法需要傳入一個(gè)回調(diào)函數(shù)作為參數(shù),該回調(diào)函數(shù)會(huì)在瀏覽器下一次重繪之前執(zhí)行,當(dāng)你準(zhǔn)備更新動(dòng)畫(huà)時(shí)你應(yīng)該調(diào)用此方法。這將使瀏覽器在下一次重繪之前調(diào)用你傳入給該方法的動(dòng)畫(huà)函數(shù) (即你的回調(diào)函數(shù))。

「注意:」 若你想在瀏覽器下次重繪之前繼續(xù)更新下一幀動(dòng)畫(huà),那么回調(diào)函數(shù)自身必須再次調(diào)用 window.requestAnimationFrame()

MDN requestAnimationFrame

「參數(shù)」

  • callback?下一次重繪之前更新動(dòng)畫(huà)幀所調(diào)用的函數(shù) (即上面所說(shuō)的回調(diào)函數(shù))。該回調(diào)函數(shù)會(huì)被傳入DOMHighResTimeStamp參數(shù),該參數(shù)與performance.now()的返回值相同,它表示requestAnimationFrame() 開(kāi)始去執(zhí)行回調(diào)函數(shù)的時(shí)刻。

「返回值」

一個(gè) long 整數(shù),請(qǐng)求 ID,是回調(diào)列表中唯一的標(biāo)識(shí)。是個(gè)非零值,沒(méi)別的意義。你可以傳這個(gè)值給 window.cancelAnimationFrame() 以取消回調(diào)函數(shù)。

測(cè)試版

通過(guò) requestAnimationFrame API可以知道,回調(diào)函數(shù)中的參數(shù)就是一個(gè) DOMHighResTimeStamp參數(shù),該參數(shù)與performance.now()的返回值相同,它表示requestAnimationFrame() 開(kāi)始去執(zhí)行回調(diào)函數(shù)的時(shí)刻。

那我們直接使用該值不就可以了嗎?試試看:








Document



hello world

這里顯示倒計(jì)時(shí)狀態(tài)









效果如下:

測(cè)試版

雖然比較簡(jiǎn)陋,但是并沒(méi)有出現(xiàn) setInterval版 的bug,接下來(lái)在一步步優(yōu)化。

簡(jiǎn)易版

我們加上格式化時(shí)間的函數(shù) parseTime() 和 parseFormat(), 代碼如下:








Document



hello world

這里顯示倒計(jì)時(shí)狀態(tài)









效果如下:

簡(jiǎn)易版

又看到了我們熟悉的時(shí)間格式啦, 格式化的方法也是來(lái)源于vant的CutDown組件中的格式化代碼!

格式化雖然是完成了,但是怎么去停止呢?能不能支持暫停、繼續(xù)、重置呢?

接下來(lái)繼續(xù)完善。

進(jìn)階版

我們直接通過(guò) window.cancelAnimationFrame() 去取消回調(diào)函數(shù)即可!在 useCountUp函數(shù)中添加一下 pause 即可!

const pause = () {
if (rafId) {
window.cancelAnimationFrame(rafId)
}
}

效果如下:

進(jìn)階版

不少的小伙伴已經(jīng)發(fā)現(xiàn),停止雖然是沒(méi)問(wèn)題了,當(dāng)再次點(diǎn)擊開(kāi)始的時(shí)候,時(shí)間怎么不對(duì)了?有瑕疵!

因?yàn)槲覀兩偎阊a(bǔ)時(shí)時(shí)間,做如下修改,添加startTime 、 stopTime 和  goOn 方法:

const useCountUp = () {
let rafId;
let startTime;
let stopTime;


const step = (timestamp) => {
console.log('timestamp', timestamp)
render(timestamp - startTime)
rafId = window.requestAnimationFrame(step)
}

const start = () {
startTime = performance.now()
rafId = window.requestAnimationFrame(step)
}

const pause = () {
stopTime = performance.now()
if (rafId) {
window.cancelAnimationFrame(rafId)
}
}

const goOn = () {
startTime += performance.now() - stopTime
rafId = window.requestAnimationFrame(step)
}

return {
start,
pause,
goOn
}
}

這里基本上已經(jīng)完成了暫停和繼續(xù)的功能了,但是仍是有些bug的,可以多次點(diǎn)擊繼續(xù)試試 。

完整版

接下來(lái),我們來(lái)修復(fù)上述的bug,方法:添加一個(gè)變量來(lái)表示當(dāng)前計(jì)時(shí)器的狀態(tài)。

在增加幾個(gè)新功能:

  • 添加 重置 方法: 其實(shí)我們只需要調(diào)用一下暫停,在清理一下時(shí)間即可
  • 支持 配置:比如 正香計(jì)時(shí)的時(shí)間, 計(jì)時(shí)結(jié)束的函數(shù)

核心代碼如下,其他部分代碼不變:

const useCountUp = (options) => {
let rafId, startTime, stopTime, curentTime, counting = false

const step = (timestamp) => {
curentTime = timestamp - startTime
render(curentTime)
options.onChange?.(curentTime);

if (options.time) {
if (Math.floor(curentTime) < options.time) {
rafId = window.requestAnimationFrame(step)
} else {
pause()
options.onFinish?.()
}
} else {
rafId = window.requestAnimationFrame(step)
}

}

const start = () {
// 計(jì)時(shí)中 或者 已經(jīng)開(kāi)始過(guò)計(jì)時(shí)想要重新開(kāi)始計(jì)時(shí),應(yīng)該先點(diǎn)擊一下 重置 再開(kāi)始計(jì)時(shí)
if (counting || curentTime) {
return
}
counting = true
startTime = performance.now()
rafId = window.requestAnimationFrame(step)
}

const pause = () {
// 已經(jīng)暫停后,屏蔽掉點(diǎn)擊
if (!counting) {
return
}
counting = false
stopTime = performance.now()
if (rafId) {
window.cancelAnimationFrame(rafId)
}
}

const goOn = () {
// 已經(jīng)在計(jì)時(shí)中,屏蔽掉點(diǎn)擊
if (counting) {
return
}
counting = true
startTime += performance.now() - stopTime
rafId = window.requestAnimationFrame(step)
}

const reset = () {
pause()
curentTime = 0
startTime = 0
stopTime = 0
render(0)
}

return {
start,
pause,
goOn,
reset
}
}

const { start, pause, goOn, reset } = useCountUp({
time: 3 * 1000,
onChange: current console.log('change', current),
onFinish: () console.log('finish'),
});

document.querySelector('.start').addEventListener('click', () => {
start()
})

document.querySelector('.pause').addEventListener('click', () => {
pause()
})

document.querySelector('.goOn').addEventListener('click', () => {
goOn()
})

document.querySelector('.reset').addEventListener('click', () => {
reset()
})

到此基本上就是實(shí)現(xiàn)了一個(gè)毫秒級(jí)的正向計(jì)時(shí)器!

vue版

只是對(duì)js的邏輯進(jìn)行了一些封裝

代碼:https://github.com/SuYxh/timer-demo

預(yù)覽:https://suyxh.github.io/timer-demo/

總結(jié)

正向毫秒級(jí)計(jì)時(shí)器主要就是利用了window.requestAnimationFrame的回調(diào)函數(shù)的參數(shù)為DOMHighResTimeStamp,且與performance.now()的返回值相同;在實(shí)現(xiàn)暫停、繼續(xù)時(shí),需要計(jì)算一下補(bǔ)時(shí)時(shí)間。


名稱欄目:基于RequestAnimationFrame實(shí)現(xiàn)高精度毫秒級(jí)正向計(jì)時(shí)器
瀏覽路徑:http://www.dlmjj.cn/article/dhspdhj.html