mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
chore(CountDown): extract useCountDown
This commit is contained in:
parent
38334b4158
commit
8e5c8f2f89
@ -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
|
||||
|
||||
|
@ -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_ |
|
||||
|
||||
### 方法
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
},
|
||||
|
149
src/count-down/use-count-down.ts
Normal file
149
src/count-down/use-count-down.ts
Normal 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,
|
||||
};
|
||||
}
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user