diff --git a/components/_util/PortalWrapper.tsx b/components/_util/PortalWrapper.tsx index aff4061c5..6f53b3740 100644 --- a/components/_util/PortalWrapper.tsx +++ b/components/_util/PortalWrapper.tsx @@ -14,8 +14,7 @@ import { } from 'vue'; import canUseDom from './canUseDom'; import ScrollLocker from '../vc-util/Dom/scrollLocker'; -import type { RafFrame } from './raf'; -import wrapperRaf from './raf'; +import raf from './raf'; let openCount = 0; const supportDom = canUseDom(); @@ -62,7 +61,7 @@ export default defineComponent({ setup(props, { slots }) { const container = ref(); const componentRef = ref(); - const rafId = ref(); + const rafId = ref(); const scrollLocker = new ScrollLocker({ container: getParent(props.getContainer) as HTMLElement, }); @@ -176,7 +175,7 @@ export default defineComponent({ nextTick(() => { if (!attachToParent()) { - rafId.value = wrapperRaf(() => { + rafId.value = raf(() => { instance.update(); }); } @@ -190,7 +189,7 @@ export default defineComponent({ openCount = visible && openCount ? openCount - 1 : openCount; } removeCurrentContainer(); - wrapperRaf.cancel(rafId.value); + raf.cancel(rafId.value); }); return () => { diff --git a/components/_util/hooks/useLayoutState.ts b/components/_util/hooks/useLayoutState.ts index 753422ded..78630a882 100644 --- a/components/_util/hooks/useLayoutState.ts +++ b/components/_util/hooks/useLayoutState.ts @@ -1,6 +1,6 @@ import type { Ref } from 'vue'; import { onBeforeUnmount, ref } from 'vue'; -import wrapperRaf from '../raf'; +import raf from '../raf'; export type Updater = (prev: State) => State; /** @@ -15,10 +15,10 @@ export function useLayoutState( let updateBatchRef = []; const rafRef = ref(); function setFrameState(updater: Updater) { - wrapperRaf.cancel(rafRef.value); + raf.cancel(rafRef.value); updateBatchRef.push(updater); - rafRef.value = wrapperRaf(() => { + rafRef.value = raf(() => { const prevBatch = updateBatchRef; // const prevState = stateRef.value; updateBatchRef = []; @@ -34,7 +34,7 @@ export function useLayoutState( } onBeforeUnmount(() => { - wrapperRaf.cancel(rafRef.value); + raf.cancel(rafRef.value); }); return [stateRef as Ref, setFrameState]; diff --git a/components/_util/raf.ts b/components/_util/raf.ts index 70e7323c5..e264f7272 100644 --- a/components/_util/raf.ts +++ b/components/_util/raf.ts @@ -1,33 +1,47 @@ -import getRequestAnimationFrame, { cancelRequestAnimationFrame } from './getRequestAnimationFrame'; +let raf = (callback: FrameRequestCallback) => +setTimeout(callback, 16); +let caf = (num: number) => clearTimeout(num); -const oriRaf = getRequestAnimationFrame(); +if (typeof window !== 'undefined' && 'requestAnimationFrame' in window) { + raf = (callback: FrameRequestCallback) => window.requestAnimationFrame(callback); + caf = (handle: number) => window.cancelAnimationFrame(handle); +} -export type RafFrame = { - id: number; -}; -// Support call raf with delay specified frame -export default function raf(callback: () => void, delayFrames = 1): { id: number } { - let restFrames: number = delayFrames; +let rafUUID = 0; +const rafIds = new Map(); - function internalCallback() { - restFrames -= 1; +function cleanup(id: number) { + rafIds.delete(id); +} - if (restFrames <= 0) { +export default function wrapperRaf(callback: () => void, times = 1): number { + rafUUID += 1; + const id = rafUUID; + + function callRef(leftTimes: number) { + if (leftTimes === 0) { + // Clean up + cleanup(id); + + // Trigger callback(); } else { - frame.id = oriRaf(internalCallback); + // Next raf + const realId = raf(() => { + callRef(leftTimes - 1); + }); + + // Bind real raf id + rafIds.set(id, realId); } } - const frame = { - id: oriRaf(internalCallback), - }; + callRef(times); - return frame; + return id; } -raf.cancel = function cancel(frame?: { id: number }) { - if (!frame) return; - - cancelRequestAnimationFrame(frame.id); +wrapperRaf.cancel = (id: number) => { + const realId = rafIds.get(id); + cleanup(realId); + return caf(realId); }; diff --git a/components/_util/throttleByAnimationFrame.ts b/components/_util/throttleByAnimationFrame.ts index 083824cfd..dd84132a4 100644 --- a/components/_util/throttleByAnimationFrame.ts +++ b/components/_util/throttleByAnimationFrame.ts @@ -1,8 +1,7 @@ -import type { RafFrame } from './raf'; import raf from './raf'; export default function throttleByAnimationFrame(fn: (...args: any[]) => void) { - let requestId: RafFrame; + let requestId: number; const later = (args: any[]) => () => { requestId = null; diff --git a/components/menu/src/PopupTrigger.tsx b/components/menu/src/PopupTrigger.tsx index fa47b4c39..1025df0d3 100644 --- a/components/menu/src/PopupTrigger.tsx +++ b/components/menu/src/PopupTrigger.tsx @@ -4,7 +4,6 @@ import { computed, defineComponent, onBeforeUnmount, ref, watch } from 'vue'; import type { MenuMode } from './interface'; import { useInjectForceRender, useInjectMenu } from './hooks/useMenuContext'; import { placements, placementsRtl } from './placements'; -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; import classNames from '../../_util/classNames'; import { getTransitionProps } from '../../_util/transition'; @@ -54,7 +53,7 @@ export default defineComponent({ const popupPlacement = computed(() => popupPlacementMap[props.mode]); - const visibleRef = ref(); + const visibleRef = ref(); watch( () => props.visible, visible => { diff --git a/components/slider/SliderTooltip.tsx b/components/slider/SliderTooltip.tsx index e11e05061..e9b070a09 100644 --- a/components/slider/SliderTooltip.tsx +++ b/components/slider/SliderTooltip.tsx @@ -1,6 +1,5 @@ import { onBeforeUnmount, watch, onActivated, defineComponent, ref } from 'vue'; import Tooltip, { tooltipProps } from '../tooltip'; -import type { RafFrame } from '../_util/raf'; import raf from '../_util/raf'; export default defineComponent({ @@ -10,7 +9,7 @@ export default defineComponent({ setup(props, { attrs, slots }) { const innerRef = ref(null); - const rafRef = ref(null); + const rafRef = ref(null); function cancelKeepAlign() { raf.cancel(rafRef.value!); diff --git a/components/tabs/src/TabNavList/index.tsx b/components/tabs/src/TabNavList/index.tsx index 03c36be14..227930239 100644 --- a/components/tabs/src/TabNavList/index.tsx +++ b/components/tabs/src/TabNavList/index.tsx @@ -22,8 +22,7 @@ import { onBeforeUnmount, defineComponent, ref, watch, watchEffect, computed } f import PropTypes from '../../../_util/vue-types'; import useSyncState from '../hooks/useSyncState'; import useState from '../../../_util/hooks/useState'; -import type { RafFrame } from '../../../_util/raf'; -import wrapperRaf from '../../../_util/raf'; +import raf from '../../../_util/raf'; import classNames from '../../../_util/classNames'; import ResizeObserver from '../../../vc-resize-observer'; import { toPx } from '../../../_util/util'; @@ -340,9 +339,9 @@ export default defineComponent({ const activeTabOffset = computed(() => tabOffsets.value.get(props.activeKey)); // Delay set ink style to avoid remove tab blink - const inkBarRafRef = ref(); + const inkBarRafRef = ref(); const cleanInkBarRaf = () => { - wrapperRaf.cancel(inkBarRafRef.value); + raf.cancel(inkBarRafRef.value); }; watch([activeTabOffset, tabPositionTopOrBottom, () => props.rtl], () => { @@ -364,7 +363,7 @@ export default defineComponent({ } cleanInkBarRaf(); - inkBarRafRef.value = wrapperRaf(() => { + inkBarRafRef.value = raf(() => { setInkStyle(newInkStyle); }); }); diff --git a/components/tabs/src/hooks/useRaf.ts b/components/tabs/src/hooks/useRaf.ts index aa80b6f5a..730d6bf54 100644 --- a/components/tabs/src/hooks/useRaf.ts +++ b/components/tabs/src/hooks/useRaf.ts @@ -1,16 +1,15 @@ import type { Ref } from 'vue'; import { ref, onBeforeUnmount } from 'vue'; -import type { RafFrame } from '../../../_util/raf'; -import wrapperRaf from '../../../_util/raf'; +import raf from '../../../_util/raf'; export default function useRaf(callback: Callback) { - const rafRef = ref(); + const rafRef = ref(); const removedRef = ref(false); function trigger(...args: any[]) { if (!removedRef.value) { - wrapperRaf.cancel(rafRef.value); - rafRef.value = wrapperRaf(() => { + raf.cancel(rafRef.value); + rafRef.value = raf(() => { callback(...args); }); } @@ -18,7 +17,7 @@ export default function useRaf(callback: Callback) { onBeforeUnmount(() => { removedRef.value = true; - wrapperRaf.cancel(rafRef.value); + raf.cancel(rafRef.value); }); return trigger; diff --git a/components/vc-image/src/hooks/useFrameSetState.ts b/components/vc-image/src/hooks/useFrameSetState.ts index 750bf3309..a2d6cf07c 100644 --- a/components/vc-image/src/hooks/useFrameSetState.ts +++ b/components/vc-image/src/hooks/useFrameSetState.ts @@ -1,4 +1,3 @@ -import type { RafFrame } from '../../../_util/raf'; import raf from '../../../_util/raf'; import { onMounted, reactive, ref } from 'vue'; @@ -6,7 +5,7 @@ type SetActionType = Partial | ((state: T) => Partial); export default function useFrameSetState( initial: T, ): [Record, (newState: SetActionType) => void] { - const frame = ref(null); + const frame = ref(null); const state = reactive({ ...initial }); const queue = ref[]>([]); diff --git a/components/vc-picker/hooks/useHoverValue.ts b/components/vc-picker/hooks/useHoverValue.ts index e39b4031d..8981c9695 100644 --- a/components/vc-picker/hooks/useHoverValue.ts +++ b/components/vc-picker/hooks/useHoverValue.ts @@ -1,4 +1,3 @@ -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; import type { ComputedRef, Ref, UnwrapRef } from 'vue'; import { ref, onBeforeUnmount, watch } from 'vue'; @@ -10,7 +9,7 @@ export default function useHoverValue( { formatList, generateConfig, locale }: ValueTextConfig, ): [ComputedRef, (date: DateType) => void, (immediately?: boolean) => void] { const innerValue = ref(null); - let rafId: RafFrame; + let rafId: number; function setValue(val: DateType, immediately = false) { raf.cancel(rafId); diff --git a/components/vc-picker/utils/uiUtil.ts b/components/vc-picker/utils/uiUtil.ts index 7bc1e9f66..7defa1571 100644 --- a/components/vc-picker/utils/uiUtil.ts +++ b/components/vc-picker/utils/uiUtil.ts @@ -1,15 +1,14 @@ import isVisible from '../../vc-util/Dom/isVisible'; import KeyCode from '../../_util/KeyCode'; -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; import type { GenerateConfig } from '../generate'; import type { CustomFormat, PanelMode, PickerMode } from '../interface'; -const scrollIds = new Map(); +const scrollIds = new Map(); /** Trigger when element is visible in view */ export function waitElementReady(element: HTMLElement, callback: () => void): () => void { - let id: RafFrame; + let id: number; function tryOrNextFrame() { if (isVisible(element)) { diff --git a/components/vc-table/Header/DragHandle.tsx b/components/vc-table/Header/DragHandle.tsx index 4c15943e7..2e75461ae 100644 --- a/components/vc-table/Header/DragHandle.tsx +++ b/components/vc-table/Header/DragHandle.tsx @@ -1,6 +1,5 @@ import addEventListenerWrap from '../../vc-util/Dom/addEventListener'; import type { EventHandler } from '../../_util/EventInterface'; -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; import { defineComponent, onUnmounted, computed, ref, watchEffect, getCurrentInstance } from 'vue'; import type { PropType } from 'vue'; @@ -74,7 +73,7 @@ export default defineComponent({ const instance = getCurrentInstance(); let baseWidth = 0; const dragging = ref(false); - let rafId: RafFrame; + let rafId: number; const updateWidth = (e: HandleEvent) => { let pageX = 0; if (e.touches) { diff --git a/components/vc-table/hooks/useFrame.ts b/components/vc-table/hooks/useFrame.ts index fd694de95..6e176052c 100644 --- a/components/vc-table/hooks/useFrame.ts +++ b/components/vc-table/hooks/useFrame.ts @@ -1,4 +1,3 @@ -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; import type { Ref, UnwrapRef } from 'vue'; import { onBeforeUnmount, ref, shallowRef } from 'vue'; @@ -9,7 +8,7 @@ export function useLayoutState( defaultState: State, ): [Ref, (updater: Updater) => void] { const stateRef = shallowRef(defaultState); - let rafId: RafFrame; + let rafId: number; const updateBatchRef = shallowRef[]>([]); function setFrameState(updater: Updater) { updateBatchRef.value.push(updater); diff --git a/components/vc-trigger/Popup/useVisibleStatus.ts b/components/vc-trigger/Popup/useVisibleStatus.ts index 74cb7f763..799ae2edd 100644 --- a/components/vc-trigger/Popup/useVisibleStatus.ts +++ b/components/vc-trigger/Popup/useVisibleStatus.ts @@ -1,6 +1,5 @@ import type { Ref } from 'vue'; import { onBeforeUnmount, ref, watch, onMounted } from 'vue'; -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; /** @@ -24,7 +23,7 @@ export default ( doMeasure: Func, ): [Ref, (callback?: () => void) => void] => { const status = ref(null); - const rafRef = ref(); + const rafRef = ref(); const destroyRef = ref(false); function setStatus(nextStatus: PopupStatus) { if (!destroyRef.value) { diff --git a/components/vc-virtual-list/hooks/useFrameWheel.ts b/components/vc-virtual-list/hooks/useFrameWheel.ts index a2f1eaf59..9efca2be7 100644 --- a/components/vc-virtual-list/hooks/useFrameWheel.ts +++ b/components/vc-virtual-list/hooks/useFrameWheel.ts @@ -1,5 +1,4 @@ import type { Ref } from 'vue'; -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; import isFF from '../utils/isFirefox'; import useOriginScroll from './useOriginScroll'; @@ -16,7 +15,7 @@ export default function useFrameWheel( onWheelDelta: (offset: number) => void, ): [(e: WheelEvent) => void, (e: FireFoxDOMMouseScrollEvent) => void] { let offsetRef = 0; - let nextFrame: RafFrame = null; + let nextFrame: number = null; // Firefox patch let wheelValue = null; diff --git a/components/vc-virtual-list/hooks/useScrollTo.tsx b/components/vc-virtual-list/hooks/useScrollTo.tsx index bfd372917..dd6bbb25a 100644 --- a/components/vc-virtual-list/hooks/useScrollTo.tsx +++ b/components/vc-virtual-list/hooks/useScrollTo.tsx @@ -1,6 +1,5 @@ import type { Data } from '../../_util/type'; import type { ComputedRef, Ref } from 'vue'; -import type { RafFrame } from '../../_util/raf'; import raf from '../../_util/raf'; import type { GetKey } from '../interface'; @@ -14,7 +13,7 @@ export default function useScrollTo( syncScrollTop: (newTop: number) => void, triggerFlash: () => void, ) { - let scroll: RafFrame = null; + let scroll: number; return (arg?: any) => { // When not argument provided, we think dev may want to show the scrollbar