<template> <view class="u-count-down"> <slot> <text class="u-count-down__text" :style="customStyle">{{ formattedTime }}</text> </slot> </view> </template> <script> import { isSameSecond, parseFormat, parseTimeData } from "./utils"; /** * u-count-down 倒计时 * @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。 * @tutorial https://uviewui.com/components/countDown.html * @property {String | Number} timestamp 倒计时时长,单位ms (默认 0 ) * @property {String} format 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 (默认 'HH:mm:ss' ) * @property {Boolean} autoStart 是否自动开始倒计时 (默认 true ) * @event {Function} end 倒计时结束时触发 * @event {Function} change 倒计时变化时触发 * @event {Function} start 开始倒计时 * @event {Function} pause 暂停倒计时 * @event {Function} reset 重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时 * @example <u-count-down :timestamp="timestamp"></u-count-down> */ export default { name: "u-count-down", emits: ["change", "end", "finish"], props: { // 倒计时时长,单位ms timestamp: { type: [String, Number], default: 0 }, // 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 format: { type: String, default: "DD:HH:mm:ss" }, // 是否自动开始倒计时 autoStart: { type: Boolean, default: true }, customStyle: { type: [String, Object], default: "" } }, data() { return { timer: null, // 各单位(天,时,分等)剩余时间 timeData: parseTimeData(0), // 格式化后的时间,如"03:23:21" formattedTime: "0", // 倒计时是否正在进行中 runing: false, endTime: 0, // 结束的毫秒时间戳 remainTime: 0 // 剩余的毫秒时间 }; }, watch: { timestamp(n) { this.reset(); }, format(newVal, oldVal) { this.pause(); this.start(); } }, mounted() { this.init(); }, methods: { init() { this.reset(); }, // 开始倒计时 start() { if (this.runing) return; // 标识为进行中 this.runing = true; // 结束时间戳 = 此刻时间戳 + 剩余的时间 this.endTime = Date.now() + this.remainTime; this.toTick(); }, // 根据是否展示毫秒,执行不同操作函数 toTick() { if (this.format.indexOf("SSS") > -1) { this.microTick(); } else { this.macroTick(); } }, macroTick() { this.clearTimeout(); // 每隔一定时间,更新一遍定时器的值 // 同时此定时器的作用也能带来毫秒级的更新 this.timer = setTimeout(() => { // 获取剩余时间 const remain = this.getRemainTime(); // 重设剩余时间 if (!isSameSecond(remain, this.remainTime) || remain === 0) { this.setRemainTime(remain); } // 如果剩余时间不为0,则继续检查更新倒计时 if (this.remainTime !== 0) { this.macroTick(); } }, 30); }, microTick() { this.clearTimeout(); this.timer = setTimeout(() => { this.setRemainTime(this.getRemainTime()); if (this.remainTime !== 0) { this.microTick(); } }, 30); }, // 获取剩余的时间 getRemainTime() { // 取最大值,防止出现小于0的剩余时间值 return Math.max(this.endTime - Date.now(), 0); }, // 设置剩余的时间 setRemainTime(remain) { this.remainTime = remain; // 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象 const timeData = parseTimeData(remain); this.$emit("change", timeData); // 得出格式化后的时间 this.formattedTime = parseFormat(this.format, timeData); // 如果时间已到,停止倒计时 if (remain <= 0) { this.pause(); this.$emit("end"); this.$emit("finish"); } }, // 重置倒计时 reset() { this.pause(); this.remainTime = this.timestamp; this.setRemainTime(this.remainTime); if (this.autoStart) { this.start(); } }, // 暂停倒计时 pause() { this.runing = false; this.clearTimeout(); }, // 清空定时器 clearTimeout() { clearTimeout(this.timer); this.timer = null; } }, // #ifndef VUE3 beforeDestroy() { this.clearTimeout(); }, // #endif // #ifdef VUE3 beforeUnmount() { this.clearTimeout(); }, // #endif }; </script> <style lang="scss"></style>