Open jtwang7 opened 3 years ago
参考文章:
知识点:
// JS 实现倒计时 class CountDown { /** * @param {Object} style - 计时器自定义样式 * @param {String} id - 实例挂载容器id;例如 '#xxx' * @param {String} endTime - 倒计时终止时间; 要求传入日期格式符合规范 * @param {Function} onReady - 倒计时完毕触发的回调 */ constructor(props) { this.fontSize = props.style.fontSize ?? '40px'; // 字体大小 this.fontFamily = props.style.fontFamily ?? 'MTC-7-Segment, YaoSuiXinShouXieTi-2'; // 字体样式; this.color = props.style.color ?? '#111'; // 字体颜色 this.id = props.id; this.endTime = props.endTime; this.onReady = props.onReady; this.spanId = 'count-down-span'; this.init(); // 初始化 } // 生成计时器文本节点 createElement = () => { const span = document.createElement('span'); span.setAttribute('id', this.spanId); span.style.setProperty('font-size', this.fontSize); // 设置字体大小 span.style.setProperty('font-family', this.fontFamily); // 设置字体样式 span.style.setProperty('color', this.color); // 设置字体颜色 return span; } // 计算并返回剩余时间 calPeriod = (endTime) => { const total = Date.parse(endTime) - Date.parse(new Date()); const seconds = Math.floor((total / 1000) % 60); // 秒 const minutes = Math.floor((total / 1000 / 60) % 60); // 分 const hours = Math.floor((total / (1000 * 60 * 60)) % 24); // 时 const days = Math.floor(total / (1000 * 60 * 60 * 24)); // 天 return { days, hours, minutes, seconds, } } // 个位数补零 addZero = (value) => { return (value < 10) ? ('0' + value) : value.toString(); } // 基于 requestAnimationFrame API 启动计时动画 setTimeInterval = (endTime, elem, target) => { const ctx = this; // 保存 this 指向 function countdown() { const curTime = new Date().getTime(); let time; if (Date.parse(endTime) - curTime > 0) { // 获取并更新倒计时时间 const timeObj = ctx.calPeriod(endTime); time = Object.entries(timeObj).reduce((prev, cur) => { if (cur[0] === 'days') { return prev + ctx.addZero(cur[1]) + '天 '; } else if (cur[0] === 'seconds') { return prev + ctx.addZero(cur[1]); } else { return prev + ctx.addZero(cur[1]) + ': ' } }, ''); // 更新 element 元素的 innerText elem.innerText = time; // 递归执行动画 window.requestAnimationFrame(countdown); } else { // 倒计时完成后 elem.innerText = ''; // 清空倒计时文本节点 elem.style.setProperty('font-family', 'Mandhor-ALEmp, YaoSuiXinShouXieTi-2'); // 更换字体样式 ctx.onReady.call(ctx, '#' + ctx.spanId); // 执行 onReady 回调 } } target.appendChild(elem); // 节点插入目标容器 window.requestAnimationFrame(countdown); // 开启动画递归 } init = () => { // 生成节点并插入文档树 const elem = this.createElement(); // 用于倒计时展示的文本节点 const target = document.querySelector(this.id); // 容器 // 开启计时器 this.setTimeInterval(this.endTime, elem, target); } } export default CountDown;
计算剩余时间:计算结束时间与当前时间之间的时间差,Date.parse(endtime) - Date.parse(new Date())。
Date.parse(endtime) - Date.parse(new Date())
注意:Date.parse() 解析给定参数字符串并返回毫秒级的时间戳,它与 +new Date() 不同在于,+new Date() 返回的时间戳更加细粒度,在毫秒级之下。
Date.parse()
+new Date()
将时间转换为可用格式:时间差是以毫秒为单位的一段时长,需要将它转化为‘天 / 时 / 分 / 秒’
添加前导 0:对于个位数的值,需要添加前置 0,例如 7 -> 07
7
07
跨页面保持时钟进度:此处我采用了 requestAnimationFrame() 来对时间计时,requestAnimationFrame() 会向动画队列推入一个回调,该回调被保证必定在浏览器重绘之前被执行,而浏览器刷新频率同步于计算机屏幕的刷新频率,因此每 16 ms 左右会重绘一次。也就是说每 16 ms 左右会执行一次 requestAnimationFrame() 的回调函数,计时及动画都是依赖于该机制实现的。此外,requestAnimationFrame() 仅会在页面激活时执行上述机制,当页面状态为 hidden 时,会自动保存当前执行位置,暂停执行回调,直到页面重新被激活(用户重新切换到该页面)。
requestAnimationFrame()
hidden
参考文章:
知识点:
源代码
要点
计算剩余时间:计算结束时间与当前时间之间的时间差,
Date.parse(endtime) - Date.parse(new Date())
。将时间转换为可用格式:时间差是以毫秒为单位的一段时长,需要将它转化为‘天 / 时 / 分 / 秒’
添加前导 0:对于个位数的值,需要添加前置 0,例如
7
->07
跨页面保持时钟进度:此处我采用了
requestAnimationFrame()
来对时间计时,requestAnimationFrame()
会向动画队列推入一个回调,该回调被保证必定在浏览器重绘之前被执行,而浏览器刷新频率同步于计算机屏幕的刷新频率,因此每 16 ms 左右会重绘一次。也就是说每 16 ms 左右会执行一次requestAnimationFrame()
的回调函数,计时及动画都是依赖于该机制实现的。此外,requestAnimationFrame()
仅会在页面激活时执行上述机制,当页面状态为hidden
时,会自动保存当前执行位置,暂停执行回调,直到页面重新被激活(用户重新切换到该页面)。