如何写一个简单又通用的倒计时函数?
关于倒计时,可能大家都遇到/写过这样的UI: 13:15:16。
嗯,一个最简单的倒计时,就是这样。
你会怎么实现这个逻辑?
下面是我的经历。
第一次遇到这样的需求时,自然而然的一路平推,非常容易的就完成了。
第二次遇到类似的需求,产品对我说要改成这样婶儿的:1天 13:15:16。没毛病,抡起键盘,一顿control c + control v
。哎呀,需求有了一点变化,还得重新加点内容。
第三次,产品说要这样:15:16。
......
于是我们看到,前端的需求呈碎片化且变更频繁,即便使用 react、vue 等框架帮助我们解耦 UI 层和数据层(这极大的提高了开发效率),但面对需求我们依然疲于奔命。
怎么解决?我认为最好的方法就是分离可变部分和不变部分。
如何识别需求中的可变和不变部分,这需要我们开发者结合实际需求、过往经验、google等进行辨别。所以,没有最好的方法,只有现阶段你自己觉得好的方法。
针对上面需求,哪些是可变的?哪些是不变的呢?
变化部分
1、表现形式,从 13:15:16 变成 1天 13:15:16 再变成 15:16
2、倒计时结束,可能会有相应的操作,比如刷新页面
3、等
不变部分
计算:输入时间值,返回符合要求的倒计时字符串。可以通过函数表示 string = format(fmt, timeValue)。
// formatTime.js
const RE_FORMAT = new RegExp('%([1-9]?)(.)', 'g')
const ONE_SECOND = 1000;
const ONE_MINUTE = ONE_SECOND * 60;
const ONE_HOUR = ONE_MINUTE * 60;
const ONE_DAY = ONE_HOUR * 24;
/**
* @param {RegExp} fmt 按照strftime的规则,但是多了个数字,比如 "%d"=>"02", "%1d" => "2",目前支持最多以天为单位的倒计时
* @param {time} diffTime 倒计时剩余时间 单位:毫秒
*
* @return {String} fmt fmt格式的字符串
*/
function format(fmt, diffTime) {
if (diffTime === void 0 || diffTime < 0) return diffTime
let day = parseInt(diffTime / ONE_DAY)
diffTime -= day * ONE_DAY
let h = parseInt(diffTime / ONE_HOUR)
diffTime -= h * ONE_HOUR
let m = parseInt(diffTime / ONE_MINUTE)
diffTime -= m * ONE_MINUTE
let s = parseInt(diffTime / ONE_SECOND)
let res = fmt.replace(RE_FORMAT, function(_, digit, val) {
switch (val) {
case 'd': // 日,01-31
return zpad(digit || 2, day);
case 'H': // 时,01-24
return zpad(digit || 2, h);
case 'M': // 分,01-59
return zpad(digit || 2, m);
case 'S': // 秒,01-59
return zpad(digit || 2, s);
case '%':
return '%';
default: // 未匹配到,保持原样
return _;
}
})
return res
}
const ZEROS = '000000000';
function zpad(n, v) {
return (ZEROS + v).substr(-n)
}
export default {
format,
zpad
}
最后,在 Vue 中完成倒计时:
import formatTime from './formatTime'
data() {
return {
dataTime: '00:00:00'
}
},
methods: {
countDown(diffTime) {
if (isNaN(diffTime) || diffTime === void 0) return
if (diffTime < 0) return // to do something
this.downTime = formatTime.format('%H:%M:%S', diffTime)
clearTimeout(this.__timer)
this.__timer = setTimeout(() => this.countDown(diffTime - 1000), 1000)
},
}
当然,你不一定非得使用 vue。
以上