From 6334d6a7a7904544ef7a3a51e710e6b64dc23e23 Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Tue, 6 Oct 2020 09:41:12 +0800 Subject: [PATCH] feat(@vant/use): add useCountDown --- packages/vant-use/src/index.ts | 1 + packages/vant-use/src/shared.ts | 1 - packages/vant-use/src/useClickAway/index.ts | 2 +- packages/vant-use/src/useCountDown/index.ts | 152 ++++++++++++++++++ .../vant-use/src/useEventListener/index.ts | 2 +- .../vant-use/src/usePageVisibility/index.ts | 2 +- packages/vant-use/src/useWindowSize/index.ts | 2 +- packages/vant-use/src/utils.ts | 30 ++++ src/count-down/use-count-down.ts | 5 +- src/count-down/utils.ts | 4 - 10 files changed, 191 insertions(+), 10 deletions(-) delete mode 100644 packages/vant-use/src/shared.ts create mode 100644 packages/vant-use/src/useCountDown/index.ts create mode 100644 packages/vant-use/src/utils.ts diff --git a/packages/vant-use/src/index.ts b/packages/vant-use/src/index.ts index 2a19802ea..1c4304710 100644 --- a/packages/vant-use/src/index.ts +++ b/packages/vant-use/src/index.ts @@ -6,3 +6,4 @@ export { useScrollParent } from './useScrollParent'; export { useEventListener } from './useEventListener'; export { usePageVisibility } from './usePageVisibility'; export { useParent, useChildren } from './useRelation'; +export * from './utils'; diff --git a/packages/vant-use/src/shared.ts b/packages/vant-use/src/shared.ts deleted file mode 100644 index 6ea623bfc..000000000 --- a/packages/vant-use/src/shared.ts +++ /dev/null @@ -1 +0,0 @@ -export const inBrowser = typeof window !== 'undefined'; diff --git a/packages/vant-use/src/useClickAway/index.ts b/packages/vant-use/src/useClickAway/index.ts index 6b41cd330..d2e039010 100644 --- a/packages/vant-use/src/useClickAway/index.ts +++ b/packages/vant-use/src/useClickAway/index.ts @@ -1,5 +1,5 @@ import { Ref, unref } from 'vue'; -import { inBrowser } from '../shared'; +import { inBrowser } from '../utils'; import { useEventListener } from '../useEventListener'; export type UseClickAwayOptions = { diff --git a/packages/vant-use/src/useCountDown/index.ts b/packages/vant-use/src/useCountDown/index.ts new file mode 100644 index 000000000..a53a51e52 --- /dev/null +++ b/packages/vant-use/src/useCountDown/index.ts @@ -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, + }; +} diff --git a/packages/vant-use/src/useEventListener/index.ts b/packages/vant-use/src/useEventListener/index.ts index a883a9202..507622288 100644 --- a/packages/vant-use/src/useEventListener/index.ts +++ b/packages/vant-use/src/useEventListener/index.ts @@ -6,7 +6,7 @@ import { onUnmounted, onDeactivated, } from 'vue'; -import { inBrowser } from '../shared'; +import { inBrowser } from '../utils'; let supportsPassive = false; if (inBrowser) { diff --git a/packages/vant-use/src/usePageVisibility/index.ts b/packages/vant-use/src/usePageVisibility/index.ts index 51f4dc82b..767192698 100644 --- a/packages/vant-use/src/usePageVisibility/index.ts +++ b/packages/vant-use/src/usePageVisibility/index.ts @@ -1,5 +1,5 @@ import { ref } from 'vue'; -import { inBrowser } from '../shared'; +import { inBrowser } from '../utils'; import { useEventListener } from '../useEventListener'; export function usePageVisibility() { diff --git a/packages/vant-use/src/useWindowSize/index.ts b/packages/vant-use/src/useWindowSize/index.ts index d5faa7d00..6a9c90662 100644 --- a/packages/vant-use/src/useWindowSize/index.ts +++ b/packages/vant-use/src/useWindowSize/index.ts @@ -1,5 +1,5 @@ import { ref } from 'vue'; -import { inBrowser } from '../shared'; +import { inBrowser } from '../utils'; import { useEventListener } from '../useEventListener'; export function useWindowSize() { diff --git a/packages/vant-use/src/utils.ts b/packages/vant-use/src/utils.ts new file mode 100644 index 000000000..5610d0112 --- /dev/null +++ b/packages/vant-use/src/utils.ts @@ -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); + }); +} diff --git a/src/count-down/use-count-down.ts b/src/count-down/use-count-down.ts index 5c4691e8d..a53a51e52 100644 --- a/src/count-down/use-count-down.ts +++ b/src/count-down/use-count-down.ts @@ -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; diff --git a/src/count-down/utils.ts b/src/count-down/utils.ts index 5e25f9134..18623f69b 100644 --- a/src/count-down/utils.ts +++ b/src/count-down/utils.ts @@ -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); -}