倒计时CountDown类


<!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>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值