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>