Vue 倒计时(countDown)组件

解析vant-ui的 count-down 源码修改的。导入即可使用。无需任何依赖。api和使用方法同vant-ui官方地址。

countdown API: https://vant-contrib.gitee.io/vant

倒计时图片

<!--
 * Copyright ©
 * #  
 * @author: zw
 * @date: 2022-07-04 
 -->


<template>
  <div class="ml-50">
<!-- 简略用法 -->
    <count-down ref="countDown" :time="time" millisecond format="HH:mm:ss:SS" :autoStart="false" />
<!-- slot自定义用法 -->
    <count-down ref="countDown" :time="time">
      <template slot-scope="scope">
        <span class="colon"></span>
        <span class="block">{{ scope.row.minutes }}</span>
        <span class="colon"></span>
        <span class="block">{{ scope.row.seconds }}</span>
      </template>
    </count-down>

    <el-button type="success" size="mini" @click="start">开始</el-button>
    <el-button type="danger" size="mini" @click="pause">暂停</el-button>
    <el-button type="warning" size="mini" @click="reset">重置</el-button>
  </div>
</template>

<script>
import countDown from '@/components/count-down';
export default {
  name: 'demo',
  components: { countDown },
  data() {
    return {
      time: 60 * 1000
    };
  },

  mounted() {

  },

  methods: {
    start() {
      this.$refs.countDown.start();
    },
    pause() {
      this.$refs.countDown.pause();
    },
    reset() {
      this.$refs.countDown.reset();
    },
    change(val) {
      console.log(val);
    }
  }
  //  End
}

</script>

<style lang='css' scoped>
</style>

<!--
 * Copyright ©
 * #  
 * @author: zw
 * @date: 2022-07-04 
 -->


<template>
  <div :class="bem()">
    <slot v-bind:row="timeData">{{ formattedTime }}</slot>
  </div>
</template>

<script>
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;

function parseTimeData(time) {
  const days = Math.floor(time / DAY);
  const hours = Math.floor(time % DAY / HOUR);
  const minutes = Math.floor(time % HOUR / MINUTE);
  const seconds = Math.floor(time % MINUTE / SECOND);
  const milliseconds = Math.floor(time % SECOND);
  return { days, hours, minutes, seconds, milliseconds };
}
function parseFormat(format, timeData) {
  var days = timeData.days;
  var hours = timeData.hours,
      minutes = timeData.minutes,
      seconds = timeData.seconds,
      milliseconds = timeData.milliseconds;

  if (format.indexOf('DD') === -1) {
    hours += days * 24;
  } else {
    format = format.replace('DD', padZero(days));
  }

  if (format.indexOf('HH') === -1) {
    minutes += hours * 60;
  } else {
    format = format.replace('HH', padZero(hours));
  }

  if (format.indexOf('mm') === -1) {
    seconds += minutes * 60;
  } else {
    format = format.replace('mm', padZero(minutes));
  }

  if (format.indexOf('ss') === -1) {
    milliseconds += seconds * 1000;
  } else {
    format = format.replace('ss', padZero(seconds));
  }

  if (format.indexOf('S') !== -1) {
    var ms = padZero(milliseconds, 3);

    if (format.indexOf('SSS') !== -1) {
      format = format.replace('SSS', ms);
    } else if (format.indexOf('SS') !== -1) {
      format = format.replace('SS', ms.slice(0, 2));
    } else {
      format = format.replace('S', ms.charAt(0));
    }
  }

  return format;
}
function isSameSecond(t1, t2) {
  return Math.floor(t1 / 1000) === Math.floor(t2 / 1000);
}
function padZero(num, targetLength) {
  if (targetLength === void 0) {
    targetLength = 2;
  }

  var str = num + '';

  while (str.length < targetLength) {
    str = '0' + str;
  }

  return str;
}
const raf = fn => window.requestAnimationFrame.call(window, fn);
const cancelRaf = id => window.cancelAnimationFrame.call(window, id);
export default {
  name: 'count-down',
  data() {
    return {
      remain: 0
    };
  },
  props: {
    millisecond: Boolean, // 毫秒级渲染
    time: { type: [Number, String], default: 0 },
    format: { type: String, default: 'HH:mm:ss' },
    autoStart: { type: Boolean, default: true }
  },
  activated() {
    if (this.keepAlivePaused) {
      this.counting = true;
      this.keepAlivePaused = false;
      this.tick();
    }
  },
  methods: {
    start() {
      if (this.counting) return;
      this.counting = true;
      this.endTime = Date.now() + this.remain;
      this.tick();
    },
    pause() {
      this.counting = false;
      cancelRaf(this.rafId);
    },
    reset() {
      this.pause();
      this.remain = +this.time;
      if (this.autoStart) {
        this.start();
      }
    },
    tick() {
      if (!(typeof window !== 'undefined')) return;
      if (this.millisecond) {
        this.microTick();
      } else {
        this.macroTick();
      }
    },
    microTick() {
      this.rafId = raf(() => {
        /* istanbul ignore if */
        // 如果呼叫结束后立即重置
        if (!this.counting) return;
        this.setRemain(this.getRemain());
        if (this.remain > 0) {
          this.microTick();
        }
      });
    },
    macroTick() {
      this.rafId = raf(() => {
        /* istanbul ignore if */
        if (!this.counting) return;// 如果呼叫结束后立即重置
        var remain = this.getRemain();

        if (!isSameSecond(remain, this.remain) || remain === 0) {
          this.setRemain(remain);
        }

        if (this.remain > 0) {
          this.macroTick();
        }
      });
    },
    getRemain() {
      return Math.max(this.endTime - Date.now(), 0);
    },
    setRemain(remain) {
      this.remain = remain;
      this.$emit('change', this.timeData);

      if (remain === 0) {
        this.pause();
        this.$emit('finish');
      }
    },
    createBem(name) {
      /**
       * bem helper
       * b() // 'button'
       * b('text') // 'button__text'
       * b({ disabled }) // 'button button--disabled'
       * b('text', { disabled }) // 'button__text button__text--disabled'
       * b(['disabled', 'primary']) // 'button button--disabled button--primary'
       */
      return function (el, mods) {
        function gen(name, mods) {
          if (!mods) return '';
          if (typeof mods === 'string') return " " + name + "--" + mods;
          if (Array.isArray(mods)) return mods.reduce((ret, item) => ret + gen(name, item), '');
          return Object.keys(mods).reduce((ret, key) => ret + (mods[key] ? gen(name, key) : ''), '');
        }

        if (el && typeof el !== 'string') {
          mods = el; el = '';
        }

        el = el ? name + "__" + el : name;
        return "" + el + gen(el, mods);
      };
    }
  },
  computed: {
    timeData() {
      return parseTimeData(this.remain);
    },
    formattedTime() {
      return parseFormat(this.format, this.timeData);
    },
    bem() {
      return (...cls) => this.createBem(this.$options.name)(...cls);
    }
  },
  watch: {
    time: {
      immediate: true,
      handler: 'reset'
    }
  },
  deactivated() {
    if (this.counting) {
      this.pause();
      this.keepAlivePaused = true;
    }
  },
  beforeDestroy() {
    this.pause();
  },
}

</script>

<style lang='css' scoped>
.count-down {
  color: #323233;
  font-size: 14px;
  line-height: 20px;
}
</style>

DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <meta http-equiv='X-UA-Compatible' content='ie=edge'>
  <title>count-downtitle>
  <script src="https://cdn.bootcdn.net/ajax/libs/Mock.js/1.0.0/mock-min.js">script>
head>

<body>
  <div class="count-dowm">div>
body>
<script type='text/javascript'>
  function parseTimeData(time) {
    const SECOND = 1000;  // 1s
    const MINUTE = 60 * SECOND; // 1分钟
    const HOUR = 60 * MINUTE;// 1小时
    const DAY = 24 * HOUR;// 1天
    const days = Math.floor(time / DAY);
    const hours = Math.floor(time % DAY / HOUR);
    const minutes = Math.floor(time % HOUR / MINUTE);
    const seconds = Math.floor(time % MINUTE / SECOND);
    const milliseconds = Math.floor(time % SECOND);
    return { days, hours, minutes, seconds, milliseconds };
  }
  function parseFormat(format, timeData) {
    var days = timeData.days;
    var hours = timeData.hours;
    var minutes = timeData.minutes;
    var seconds = timeData.seconds;
    var milliseconds = timeData.milliseconds;

    if (format.indexOf('DD') === -1) {
      hours += days * 24;
    } else {
      format = format.replace('DD', padZero(days));
    }

    if (format.indexOf('HH') === -1) {
      minutes += hours * 60;
    } else {
      format = format.replace('HH', padZero(hours));
    }

    if (format.indexOf('mm') === -1) {
      seconds += minutes * 60;
    } else {
      format = format.replace('mm', padZero(minutes));
    }

    if (format.indexOf('ss') === -1) {
      milliseconds += seconds * 1000;
    } else {
      format = format.replace('ss', padZero(seconds));
    }

    if (format.indexOf('S') !== -1) {
      var ms = padZero(milliseconds, 3);

      if (format.indexOf('SSS') !== -1) {
        format = format.replace('SSS', ms);
      } else if (format.indexOf('SS') !== -1) {
        format = format.replace('SS', ms.slice(0, 2));
      } else {
        format = format.replace('S', ms.charAt(0));
      }
    }

    return format;
  }
  function padZero(num, targetLength) {
    if (targetLength === undefined) targetLength = 2;
    let str = num + '';
    while (str.length < targetLength) {
      str = '0' + str;
    }
    return str;
  }

  const isSameSecond = (t1, t2) => Math.floor(t1 / 1000) === Math.floor(t2 / 1000);
  let rafId = null;
  let remain = 0;
  let counting = true;
  let endTime = 0;
  let time = 8000;
  let format = "HH:mm:ss";

  function start(n) {
    if (counting) return;
    counting = true;
    endTime = Date.now() + remain;
    n > 1 ? macroTick() : microTick();
    return console.time('count-down');
  }

  function microTick() {  // 非毫秒级
    rafId = window.requestAnimationFrame(() => {
      if (!counting) return;
      format = 'HH:mm:ss';
      setRemain(getRemain())

      if (remain > 0) {
        microTick();
      }

    });
  }

  function macroTick() {  // 毫秒级
    rafId = window.requestAnimationFrame(() => {
      if (!counting) return;
      format = 'HH:mm:ss:SSS';
      const __remain = getRemain();

      if (!isSameSecond(__remain, remain) || __remain === 0) {
        setRemain(__remain);
      }

      if (remain > 0) {
        macroTick();
      }

    });
  }

  function pause() {
    counting = false;
    window.cancelAnimationFrame(rafId);
    console.timeEnd('count-down');
    console.log('count-down:pause');
  }

  function reset() {
    pause();
    remain = +time;
  }

  function render(n) {
    document.getElementsByTagName('div')[0].innerHTML = parseFormat(format, parseTimeData(n));
  }

  function setRemain(__remain) {
    remain = __remain;
    if (__remain === 0) pause();
  }

  function getRemain() {
    const n = Math.max(endTime - Date.now(), 0);
    render(n);
    return n;
  }
  reset();
  start(2);
  setTimeout(() => {
    pause();
    setTimeout(start.bind(null, 2), 1000);
  }, 3000);

script>

html>

本文严谨转载

你可能感兴趣的:(vue,vue.js,javascript,前端)