chore(CountDown): extract useCountDown

This commit is contained in:
chenjiahan 2020-10-06 09:22:54 +08:00
parent 38334b4158
commit 8e5c8f2f89
5 changed files with 203 additions and 167 deletions

View File

@ -134,26 +134,27 @@ export default {
### Events
| Event | Description | Arguments |
| --------------- | ---------------------------------- | -------------------- |
| finish | Triggered when count down finished | - |
| change `v2.4.4` | Triggered when count down changed | _timeData: TimeData_ |
| Event | Description | Arguments |
| --- | --- | --- |
| finish | Triggered when count down finished | - |
| change `v2.4.4` | Triggered when count down changed | _currentTime: CurrentTime_ |
### Slots
| Name | Description | SlotProps |
| ------- | -------------- | -------------------- |
| default | Custom Content | _timeData: TimeData_ |
| Name | Description | SlotProps |
| ------- | -------------- | -------------------------- |
| default | Custom Content | _currentTime: CurrentTime_ |
### TimeData Structure
| Name | Description | Type |
| ------------ | ------------------- | -------- |
| days | Remain days | _number_ |
| hours | Remain hours | _number_ |
| minutes | Remain minutes | _number_ |
| seconds | Remain seconds | _number_ |
| milliseconds | Remain milliseconds | _number_ |
| Name | Description | Type |
| ------------ | ----------------------------- | -------- |
| total | Total time, unit milliseconds | _number_ |
| days | Remain days | _number_ |
| hours | Remain hours | _number_ |
| minutes | Remain minutes | _number_ |
| seconds | Remain seconds | _number_ |
| milliseconds | Remain milliseconds | _number_ |
### Methods

View File

@ -144,26 +144,27 @@ export default {
### Events
| 事件名 | 说明 | 回调参数 |
| --------------- | ---------------- | -------------------- |
| finish | 倒计时结束时触发 | - |
| change `v2.4.4` | 倒计时变化时触发 | _timeData: TimeData_ |
| 事件名 | 说明 | 回调参数 |
| --------------- | ---------------- | -------------------------- |
| finish | 倒计时结束时触发 | - |
| change `v2.4.4` | 倒计时变化时触发 | _currentTime: CurrentTime_ |
### Slots
| 名称 | 说明 | SlotProps |
| ------- | ---------- | -------------------- |
| default | 自定义内容 | _timeData: TimeData_ |
| 名称 | 说明 | SlotProps |
| ------- | ---------- | -------------------------- |
| default | 自定义内容 | _currentTime: CurrentTime_ |
### TimeData 格式
### CurrentTime 格式
| 名称 | 说明 | 类型 |
| ------------ | -------- | -------- |
| days | 剩余天数 | _number_ |
| hours | 剩余小时 | _number_ |
| minutes | 剩余分钟 | _number_ |
| seconds | 剩余秒数 | _number_ |
| milliseconds | 剩余毫秒 | _number_ |
| 名称 | 说明 | 类型 |
| ------------ | ---------------------- | -------- |
| total | 剩余总时间(单位毫秒) | _number_ |
| days | 剩余天数 | _number_ |
| hours | 剩余小时 | _number_ |
| minutes | 剩余分钟 | _number_ |
| seconds | 剩余秒数 | _number_ |
| milliseconds | 剩余毫秒 | _number_ |
### 方法

View File

@ -1,18 +1,12 @@
import {
ref,
watch,
computed,
onActivated,
onDeactivated,
onBeforeUnmount,
} from 'vue';
import { watch, computed } from 'vue';
// Utils
import { raf, cancelRaf, createNamespace } from '../utils';
import { isSameSecond, parseTimeData, parseFormat } from './utils';
import { createNamespace } from '../utils';
import { parseFormat } from './utils';
// Composition
import { useExpose } from '../composition/use-expose';
import { useCountDown } from './use-count-down';
const [createComponent, bem] = createNamespace('count-down');
@ -36,118 +30,37 @@ export default createComponent({
emits: ['change', 'finish'],
setup(props, { emit, slots }) {
let rafId;
let endTime;
let counting;
let keepAlived;
const remain = ref(0);
const timeData = computed(() => parseTimeData(remain.value));
const timeText = computed(() => parseFormat(props.format, timeData.value));
const pause = () => {
counting = false;
cancelRaf(rafId);
};
const getCurrentRemain = () => Math.max(endTime - Date.now(), 0);
const setRemain = (value) => {
remain.value = value;
emit('change', timeData.value);
if (value === 0) {
pause();
const { start, pause, reset, current } = useCountDown({
time: +props.time,
millisecond: props.millisecond,
onChange(current) {
emit('change', current);
},
onFinish() {
emit('finish');
}
};
},
});
const microTick = () => {
rafId = raf(() => {
// in case of call reset immediately after finish
if (counting) {
setRemain(getCurrentRemain());
if (remain.value > 0) {
microTick();
}
}
});
};
const macroTick = () => {
rafId = raf(() => {
// in case of call reset immediately after finish
if (counting) {
const currentRemain = getCurrentRemain();
if (
!isSameSecond(currentRemain, remain.value) ||
currentRemain === 0
) {
setRemain(currentRemain);
}
if (remain.value > 0) {
macroTick();
}
}
});
};
const tick = () => {
if (props.millisecond) {
microTick();
} else {
macroTick();
}
};
const start = () => {
if (!counting) {
endTime = Date.now() + remain.value;
counting = true;
tick();
}
};
const reset = () => {
pause();
remain.value = +props.time;
const timeText = computed(() => parseFormat(props.format, current.value));
const resetTime = () => {
reset(+props.time);
if (props.autoStart) {
start();
}
};
watch(() => props.time, reset, { immediate: true });
onActivated(() => {
if (keepAlived) {
counting = true;
keepAlived = false;
tick();
}
});
onDeactivated(() => {
if (counting) {
pause();
keepAlived = true;
}
});
onBeforeUnmount(pause);
watch(() => props.time, resetTime, { immediate: true });
useExpose({
start,
reset,
pause,
reset: resetTime,
});
return () => (
<div class={bem()}>
{slots.default ? slots.default(timeData.value) : timeText.value}
{slots.default ? slots.default(current.value) : timeText.value}
</div>
);
},

View File

@ -0,0 +1,149 @@
import {
ref,
computed,
onActivated,
onDeactivated,
onBeforeUnmount,
} from 'vue';
import { raf, cancelRaf } from '../utils';
import { isSameSecond } from './utils';
export type CurrentTime = {
days: number;
hours: number;
total: number;
minutes: number;
seconds: number;
milliseconds: number;
};
export type UseCountDownOptions = {
time: number;
millisecond?: boolean;
onChange?: (current: CurrentTime) => void;
onFinish?: () => void;
};
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
export function parseTime(time: number): CurrentTime {
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 {
total: time,
days,
hours,
minutes,
seconds,
milliseconds,
};
}
export function useCountDown(options: UseCountDownOptions) {
let rafId: number;
let endTime: number;
let counting: boolean;
let deactivated: boolean;
const remain = ref(options.time);
const current = computed(() => parseTime(remain.value));
const pause = () => {
counting = false;
cancelRaf(rafId);
};
const getCurrentRemain = () => Math.max(endTime - Date.now(), 0);
const setRemain = (value: number) => {
remain.value = value;
options.onChange?.(current.value);
if (value === 0) {
pause();
options.onFinish?.();
}
};
const microTick = () => {
rafId = raf(() => {
// in case of call reset immediately after finish
if (counting) {
setRemain(getCurrentRemain());
if (remain.value > 0) {
microTick();
}
}
});
};
const macroTick = () => {
rafId = raf(() => {
// in case of call reset immediately after finish
if (counting) {
const remainRemain = getCurrentRemain();
if (!isSameSecond(remainRemain, remain.value) || remainRemain === 0) {
setRemain(remainRemain);
}
if (remain.value > 0) {
macroTick();
}
}
});
};
const tick = () => {
if (options.millisecond) {
microTick();
} else {
macroTick();
}
};
const start = () => {
if (!counting) {
endTime = Date.now() + remain.value;
counting = true;
tick();
}
};
const reset = (totalTime: number) => {
pause();
remain.value = totalTime;
};
onBeforeUnmount(pause);
onActivated(() => {
if (deactivated) {
counting = true;
deactivated = false;
tick();
}
});
onDeactivated(() => {
if (counting) {
pause();
deactivated = true;
}
});
return {
start,
pause,
reset,
current,
};
}

View File

@ -1,37 +1,9 @@
import { padZero } from '../utils';
import { CurrentTime } from './use-count-down';
export type TimeData = {
days: number;
hours: number;
minutes: number;
seconds: number;
milliseconds: number;
};
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
export function parseTimeData(time: number): TimeData {
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,
};
}
export function parseFormat(format: string, timeData: TimeData): string {
const { days } = timeData;
let { hours, minutes, seconds, milliseconds } = timeData;
export function parseFormat(format: string, currentTime: CurrentTime): string {
const { days } = currentTime;
let { hours, minutes, seconds, milliseconds } = currentTime;
if (format.indexOf('DD') === -1) {
hours += days * 24;