如何写一个简单又通用的倒计时函数?

关于倒计时,可能大家都遇到/写过这样的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。

以上

posted @ 2018-11-23 18:07  Liaofy  阅读(1010)  评论(0编辑  收藏  举报