mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(@vant/use): add useCountDown
This commit is contained in:
parent
8e5c8f2f89
commit
6334d6a7a7
@ -6,3 +6,4 @@ export { useScrollParent } from './useScrollParent';
|
|||||||
export { useEventListener } from './useEventListener';
|
export { useEventListener } from './useEventListener';
|
||||||
export { usePageVisibility } from './usePageVisibility';
|
export { usePageVisibility } from './usePageVisibility';
|
||||||
export { useParent, useChildren } from './useRelation';
|
export { useParent, useChildren } from './useRelation';
|
||||||
|
export * from './utils';
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export const inBrowser = typeof window !== 'undefined';
|
|
@ -1,5 +1,5 @@
|
|||||||
import { Ref, unref } from 'vue';
|
import { Ref, unref } from 'vue';
|
||||||
import { inBrowser } from '../shared';
|
import { inBrowser } from '../utils';
|
||||||
import { useEventListener } from '../useEventListener';
|
import { useEventListener } from '../useEventListener';
|
||||||
|
|
||||||
export type UseClickAwayOptions = {
|
export type UseClickAwayOptions = {
|
||||||
|
152
packages/vant-use/src/useCountDown/index.ts
Normal file
152
packages/vant-use/src/useCountDown/index.ts
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
import {
|
||||||
|
ref,
|
||||||
|
computed,
|
||||||
|
onActivated,
|
||||||
|
onDeactivated,
|
||||||
|
onBeforeUnmount,
|
||||||
|
} from 'vue';
|
||||||
|
import { raf, cancelRaf } 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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSameSecond(time1: number, time2: number): boolean {
|
||||||
|
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
@ -6,7 +6,7 @@ import {
|
|||||||
onUnmounted,
|
onUnmounted,
|
||||||
onDeactivated,
|
onDeactivated,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { inBrowser } from '../shared';
|
import { inBrowser } from '../utils';
|
||||||
|
|
||||||
let supportsPassive = false;
|
let supportsPassive = false;
|
||||||
if (inBrowser) {
|
if (inBrowser) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { inBrowser } from '../shared';
|
import { inBrowser } from '../utils';
|
||||||
import { useEventListener } from '../useEventListener';
|
import { useEventListener } from '../useEventListener';
|
||||||
|
|
||||||
export function usePageVisibility() {
|
export function usePageVisibility() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { inBrowser } from '../shared';
|
import { inBrowser } from '../utils';
|
||||||
import { useEventListener } from '../useEventListener';
|
import { useEventListener } from '../useEventListener';
|
||||||
|
|
||||||
export function useWindowSize() {
|
export function useWindowSize() {
|
||||||
|
30
packages/vant-use/src/utils.ts
Normal file
30
packages/vant-use/src/utils.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export const inBrowser = typeof window !== 'undefined';
|
||||||
|
|
||||||
|
const root = (inBrowser ? window : global) as Window;
|
||||||
|
|
||||||
|
let prev = Date.now();
|
||||||
|
|
||||||
|
function rafPolyfill(fn: FrameRequestCallback): number {
|
||||||
|
const curr = Date.now();
|
||||||
|
const ms = Math.max(0, 16 - (curr - prev));
|
||||||
|
const id = setTimeout(fn, ms);
|
||||||
|
prev = curr + ms;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function raf(fn: FrameRequestCallback): number {
|
||||||
|
const requestAnimationFrame = root.requestAnimationFrame || rafPolyfill;
|
||||||
|
return requestAnimationFrame.call(root, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cancelRaf(id: number) {
|
||||||
|
const cancelAnimationFrame = root.cancelAnimationFrame || root.clearTimeout;
|
||||||
|
cancelAnimationFrame.call(root, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// double raf for animation
|
||||||
|
export function doubleRaf(fn: FrameRequestCallback): void {
|
||||||
|
raf(() => {
|
||||||
|
raf(fn);
|
||||||
|
});
|
||||||
|
}
|
@ -6,7 +6,6 @@ import {
|
|||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
} from 'vue';
|
} from 'vue';
|
||||||
import { raf, cancelRaf } from '../utils';
|
import { raf, cancelRaf } from '../utils';
|
||||||
import { isSameSecond } from './utils';
|
|
||||||
|
|
||||||
export type CurrentTime = {
|
export type CurrentTime = {
|
||||||
days: number;
|
days: number;
|
||||||
@ -46,6 +45,10 @@ export function parseTime(time: number): CurrentTime {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSameSecond(time1: number, time2: number): boolean {
|
||||||
|
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
export function useCountDown(options: UseCountDownOptions) {
|
export function useCountDown(options: UseCountDownOptions) {
|
||||||
let rafId: number;
|
let rafId: number;
|
||||||
let endTime: number;
|
let endTime: number;
|
||||||
|
@ -43,7 +43,3 @@ export function parseFormat(format: string, currentTime: CurrentTime): string {
|
|||||||
|
|
||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSameSecond(time1: number, time2: number): boolean {
|
|
||||||
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user