<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>倒计时组件必备</title>
</head>
<body>
<div id="container">
container
</div>
<button type="button" id="start">开始</button> -
<button type="button" id="pause">暂停</button> -
<button type="button" id="reset">重置</button>
<script>
function parseTimeData(dateDiff) {
// dateDiff: 单位是毫秒
const d = Math.floor(dateDiff / 1000 / 60 / 60 / 24);
const h = Math.floor(dateDiff / 1000 / 60 / 60 % 24);
const m = Math.floor(dateDiff / 1000 / 60 % 60);
const s = Math.floor(dateDiff / 1000 % 60);
const mm = Math.floor(dateDiff % 1000);
return {
d,h,m,s,mm
}
}
// 格式化dataTime,替换format
function parseFormat(dataTime, format) {
if(!format) return
const o = {
'd+': dataTime.d,
'h+': dataTime.h,
'm+': dataTime.m,
's+': dataTime.s
}
for (const key in o) {
if(new RegExp('(' + key + ')').test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? o[key] : ('00' + o[key]).substr(('' + o[key]).length ) )
}
}
return format
};
// 问题记录: 如果这里的时间间隔写成一秒在回调的话,通过获取当前时间戳计算出来的remain总是不准确,有时候是1s正常的,有时候是2s,还有的时候是0s,使用没30ms调用用户毫秒级渲染,通过判断完成秒级渲染。
function simpleTick(fn) {
return setTimeout(fn, 30);
}
// 用来判断是否是很短的时间间隔 1s之内
function isSameSecond(time1, time2) {
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
}
class CountDown {
constructor(remain, format, millisecond) {
// props 状态不变的 time 重置的时候需要该状态 number
this.constantTime = remain
// 倒计时剩余时间 单位毫秒 number
this.remain = remain
// 是否开启毫秒级渲染 boolean
this.millisecond = millisecond
// 重置后自动启动 boolean
this.autoplay = true
// 为timeDate格式化替换的字符串 string
this.format = format
// 定时器id
this.timerId = null
this.timeData = parseTimeData(0)
this.formattedTime = parseFormat(this.timeData, this.format)
// 用户渲染页面
this.render();
}
// 开始
start() {
if(this.counting) {
return
}
this.counting = true
this.endTime = Date.now() + this.remain
this.tick()
}
// 停止
pause() {
this.counting = false
this.clearTimeout()
}
// 重置
reset() {
this.pause()
this.setRemain(this.constantTime)
this.render()
if(this.autoplay) {
this.start()
}
}
tick() {
if(this.millisecond) {
this.microTick()
}else {
this.macroTick()
}
}
// 毫秒级渲染
microTick() {
this.timerId = simpleTick(function () {
this.setRemain(this.getRemain());
this.render()
if (this.remain !== 0) {
this.microTick();
}
});
}
// 1s间隔渲染
macroTick () {
this.timerId = simpleTick( () => {
var remain = this.getRemain();
// 用于判断是否是1s间隔之内的,如果是不渲染,否则渲染
if (!isSameSecond(remain, this.remain) || remain === 0) {
this.setRemain(remain);
this.render()
}
if (this.remain !== 0) {
this.macroTick();
}
});
}
render() {
this.timeData = parseTimeData(this.remain)
this.formattedTime = parseFormat(this.timeData, this.format)
container.innerHTML = this.formattedTime
if (this.remain === 0) {
this.pause();
}
}
getRemain() {
return Math.max(this.endTime - Date.now());
}
setRemain(remain) {
this.remain = remain;
}
clearTimeout() {
clearTimeout(this.timerId)
this.timerId = null
}
}
const start = document.querySelector('#start');
const pause = document.querySelector('#pause');
const reset = document.querySelector('#reset');
const container = document.querySelector('#container');
const countDown = new CountDown(3600 * 1140, 'dd天hh时mm分ss秒');
start.addEventListener('click', function () {
countDown.start()
});
pause.addEventListener('click', function () {
countDown.pause()
});
reset.addEventListener('click', function () {
countDown.reset()
});
// 遇见的问题
let testStart = new Date().getTime()
console.log('testStart: ' + Math.floor(testStart / 1000))
setInterval(() => {
let end = new Date().getTime();
console.log('end: ' + Math.floor(end / 1000))
// console.log(end - testStart)
console.log(Math.floor(end / 1000) - Math.floor(testStart / 1000))
testStart = end
}, 1000);
/*
需求是:每间隔1s获取时间戳 end - testStart 看看是不是1000,
1:经过测试直接使用 end - testStart算出来的结果不对,1001 998 999 345等等等,不清楚原因。
2:正确的做法,把毫秒除以 1000换算为秒,向下取整,算出来的结果也不对有"1", "0", " 2"。
问题记录: 如果这里的时间间隔写成一秒在回调的话,通过获取当前时间戳计算出来的remain总是不准确,有时候是1s正常的,有时候是2s,还有的时候是0s。
解决:
查看vant源码发现,定时器30ms执行,如果时毫秒级渲染那就直接调用render函数,如果时秒级渲染,通过utils.isSameSecond函数
判断当前倒计时的 remain和 最新定时器回调函数得到的remain是不是在1s间隔之内,如果时之外,渲染。
*/
</script>
</body>
</html>
倒计时CountDown类
最新推荐文章于 2022-03-11 11:28:52 发布