feat(@vant/use): add useCountDown

This commit is contained in:
chenjiahan 2020-10-06 09:41:12 +08:00
parent 8e5c8f2f89
commit 6334d6a7a7
10 changed files with 191 additions and 10 deletions

View File

@ -6,3 +6,4 @@ export { useScrollParent } from './useScrollParent';
export { useEventListener } from './useEventListener';
export { usePageVisibility } from './usePageVisibility';
export { useParent, useChildren } from './useRelation';
export * from './utils';

View File

@ -1 +0,0 @@
export const inBrowser = typeof window !== 'undefined';

View File

@ -1,5 +1,5 @@
import { Ref, unref } from 'vue';
import { inBrowser } from '../shared';
import { inBrowser } from '../utils';
import { useEventListener } from '../useEventListener';
export type UseClickAwayOptions = {

View 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,
};
}

View File

@ -6,7 +6,7 @@ import {
onUnmounted,
onDeactivated,
} from 'vue';
import { inBrowser } from '../shared';
import { inBrowser } from '../utils';
let supportsPassive = false;
if (inBrowser) {

View File

@ -1,5 +1,5 @@
import { ref } from 'vue';
import { inBrowser } from '../shared';
import { inBrowser } from '../utils';
import { useEventListener } from '../useEventListener';
export function usePageVisibility() {

View File

@ -1,5 +1,5 @@
import { ref } from 'vue';
import { inBrowser } from '../shared';
import { inBrowser } from '../utils';
import { useEventListener } from '../useEventListener';
export function useWindowSize() {

View 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);
});
}

View File

@ -6,7 +6,6 @@ import {
onBeforeUnmount,
} from 'vue';
import { raf, cancelRaf } from '../utils';
import { isSameSecond } from './utils';
export type CurrentTime = {
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) {
let rafId: number;
let endTime: number;

View File

@ -43,7 +43,3 @@ export function parseFormat(format: string, currentTime: CurrentTime): string {
return format;
}
export function isSameSecond(time1: number, time2: number): boolean {
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000);
}