1. 程式人生 > >如何寫一個簡單又通用的倒計時函式?

如何寫一個簡單又通用的倒計時函式?

關於倒計時,可能大家都遇到/寫過這樣的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。

以上