|
|
|
|
<template>
|
|
|
|
|
<view class="u-count-down">
|
|
|
|
|
<slot>
|
|
|
|
|
<text class="u-count-down__text">{{ formattedTime }}</text>
|
|
|
|
|
</slot>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { props } from './props';
|
|
|
|
|
import { mpMixin } from '../../libs/mixin/mpMixin';
|
|
|
|
|
import { mixin } from '../../libs/mixin/mixin';
|
|
|
|
|
import {
|
|
|
|
|
isSameSecond,
|
|
|
|
|
parseFormat,
|
|
|
|
|
parseTimeData
|
|
|
|
|
} from './utils';
|
|
|
|
|
/**
|
|
|
|
|
* u-count-down 倒计时
|
|
|
|
|
* @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。
|
|
|
|
|
* @tutorial https://uview-plus.jiangruyi.com/components/countDown.html
|
|
|
|
|
* @property {String | Number} time 倒计时时长,单位ms (默认 0 )
|
|
|
|
|
* @property {String} format 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 (默认 'HH:mm:ss' )
|
|
|
|
|
* @property {Boolean} autoStart 是否自动开始倒计时 (默认 true )
|
|
|
|
|
* @property {Boolean} millisecond 是否展示毫秒倒计时 (默认 false )
|
|
|
|
|
* @event {Function} finish 倒计时结束时触发
|
|
|
|
|
* @event {Function} change 倒计时变化时触发
|
|
|
|
|
* @event {Function} start 开始倒计时
|
|
|
|
|
* @event {Function} pause 暂停倒计时
|
|
|
|
|
* @event {Function} reset 重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时
|
|
|
|
|
* @example <u-count-down :time="time"></u-count-down>
|
|
|
|
|
*/
|
|
|
|
|
export default {
|
|
|
|
|
name: 'u-count-down',
|
|
|
|
|
mixins: [mpMixin, mixin, props],
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
timer: null,
|
|
|
|
|
// 各单位(天,时,分等)剩余时间
|
|
|
|
|
timeData: parseTimeData(0),
|
|
|
|
|
// 格式化后的时间,如"03:23:21"
|
|
|
|
|
formattedTime: '0',
|
|
|
|
|
// 倒计时是否正在进行中
|
|
|
|
|
runing: false,
|
|
|
|
|
endTime: 0, // 结束的毫秒时间戳
|
|
|
|
|
remainTime: 0, // 剩余的毫秒时间
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
time(n) {
|
|
|
|
|
this.reset()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
mounted() {
|
|
|
|
|
this.init()
|
|
|
|
|
},
|
|
|
|
|
emits: ["change", "finish"],
|
|
|
|
|
methods: {
|
|
|
|
|
init() {
|
|
|
|
|
this.reset()
|
|
|
|
|
},
|
|
|
|
|
// 开始倒计时
|
|
|
|
|
start() {
|
|
|
|
|
if (this.runing) return
|
|
|
|
|
// 标识为进行中
|
|
|
|
|
this.runing = true
|
|
|
|
|
// 结束时间戳 = 此刻时间戳 + 剩余的时间
|
|
|
|
|
this.endTime = Date.now() + this.remainTime
|
|
|
|
|
this.toTick()
|
|
|
|
|
},
|
|
|
|
|
// 根据是否展示毫秒,执行不同操作函数
|
|
|
|
|
toTick() {
|
|
|
|
|
if (this.millisecond) {
|
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
}, 50)
|
|
|
|
|
},
|
|
|
|
|
// 获取剩余的时间
|
|
|
|
|
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('finish')
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 重置倒计时
|
|
|
|
|
reset() {
|
|
|
|
|
this.pause()
|
|
|
|
|
this.remainTime = this.time
|
|
|
|
|
this.setRemainTime(this.remainTime)
|
|
|
|
|
if (this.autoStart) {
|
|
|
|
|
this.start()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
// 暂停倒计时
|
|
|
|
|
pause() {
|
|
|
|
|
this.runing = false;
|
|
|
|
|
this.clearTimeout()
|
|
|
|
|
},
|
|
|
|
|
// 清空定时器
|
|
|
|
|
clearTimeout() {
|
|
|
|
|
clearTimeout(this.timer)
|
|
|
|
|
this.timer = null
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
beforeUnmount() {
|
|
|
|
|
this.clearTimeout()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style
|
|
|
|
|
lang="scss"
|
|
|
|
|
scoped
|
|
|
|
|
>
|
|
|
|
|
@import "../../libs/css/components.scss";
|
|
|
|
|
$u-count-down-text-color:$u-content-color !default;
|
|
|
|
|
$u-count-down-text-font-size:15px !default;
|
|
|
|
|
$u-count-down-text-line-height:22px !default;
|
|
|
|
|
|
|
|
|
|
.u-count-down {
|
|
|
|
|
&__text {
|
|
|
|
|
color: $u-count-down-text-color;
|
|
|
|
|
font-size: $u-count-down-text-font-size;
|
|
|
|
|
line-height: $u-count-down-text-line-height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|