You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

175 lines
4.4 KiB

<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>