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 ### Events
| Event | Description | Arguments | | Event | Description | Arguments |
| --------------- | ---------------------------------- | -------------------- | | --- | --- | --- |
| finish | Triggered when count down finished | - | | finish | Triggered when count down finished | - |
| change `v2.4.4` | Triggered when count down changed | _timeData: TimeData_ | | change `v2.4.4` | Triggered when count down changed | _currentTime: CurrentTime_ |
### Slots ### Slots
| Name | Description | SlotProps | | Name | Description | SlotProps |
| ------- | -------------- | -------------------- | | ------- | -------------- | -------------------------- |
| default | Custom Content | _timeData: TimeData_ | | default | Custom Content | _currentTime: CurrentTime_ |
### TimeData Structure ### TimeData Structure
| Name | Description | Type | | Name | Description | Type |
| ------------ | ------------------- | -------- | | ------------ | ----------------------------- | -------- |
| days | Remain days | _number_ | | total | Total time, unit milliseconds | _number_ |
| hours | Remain hours | _number_ | | days | Remain days | _number_ |
| minutes | Remain minutes | _number_ | | hours | Remain hours | _number_ |
| seconds | Remain seconds | _number_ | | minutes | Remain minutes | _number_ |
| milliseconds | Remain milliseconds | _number_ | | seconds | Remain seconds | _number_ |
| milliseconds | Remain milliseconds | _number_ |
### Methods ### Methods

View File

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

View File

@ -1,18 +1,12 @@
import { import { watch, computed } from 'vue';
ref,
watch,
computed,
onActivated,
onDeactivated,
onBeforeUnmount,
} from 'vue';
// Utils // Utils
import { raf, cancelRaf, createNamespace } from '../utils'; import { createNamespace } from '../utils';
import { isSameSecond, parseTimeData, parseFormat } from './utils'; import { parseFormat } from './utils';
// Composition // Composition
import { useExpose } from '../composition/use-expose'; import { useExpose } from '../composition/use-expose';
import { useCountDown } from './use-count-down';
const [createComponent, bem] = createNamespace('count-down'); const [createComponent, bem] = createNamespace('count-down');
@ -36,118 +30,37 @@ export default createComponent({
emits: ['change', 'finish'], emits: ['change', 'finish'],
setup(props, { emit, slots }) { setup(props, { emit, slots }) {
let rafId; const { start, pause, reset, current } = useCountDown({
let endTime; time: +props.time,
let counting; millisecond: props.millisecond,
let keepAlived; onChange(current) {
emit('change', current);
const remain = ref(0); },
const timeData = computed(() => parseTimeData(remain.value)); onFinish() {
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();
emit('finish'); emit('finish');
} },
}; });
const microTick = () => { const timeText = computed(() => parseFormat(props.format, current.value));
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 resetTime = () => {
reset(+props.time);
if (props.autoStart) { if (props.autoStart) {
start(); start();
} }
}; };
watch(() => props.time, reset, { immediate: true }); watch(() => props.time, resetTime, { immediate: true });
onActivated(() => {
if (keepAlived) {
counting = true;
keepAlived = false;
tick();
}
});
onDeactivated(() => {
if (counting) {
pause();
keepAlived = true;
}
});
onBeforeUnmount(pause);
useExpose({ useExpose({
start, start,
reset,
pause, pause,
reset: resetTime,
}); });
return () => ( return () => (
<div class={bem()}> <div class={bem()}>
{slots.default ? slots.default(timeData.value) : timeText.value} {slots.default ? slots.default(current.value) : timeText.value}
</div> </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 { padZero } from '../utils';
import { CurrentTime } from './use-count-down';
export type TimeData = { export function parseFormat(format: string, currentTime: CurrentTime): string {
days: number; const { days } = currentTime;
hours: number; let { hours, minutes, seconds, milliseconds } = currentTime;
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;
if (format.indexOf('DD') === -1) { if (format.indexOf('DD') === -1) {
hours += days * 24; hours += days * 24;