fix: requestAnimationFrame error in ssr, close #4833

pull/4839/head^2
tangjinzhou 2021-11-02 16:07:35 +08:00
parent 7b03f8a739
commit a938bcb4de
15 changed files with 57 additions and 45 deletions

View File

@ -1,5 +1,6 @@
import cssAnimation from './css-animation';
import { nextTick } from 'vue';
import { requestAnimationTimeout, cancelAnimationTimeout } from './requestAnimationTimeout';
function animate(node, show, done) {
let height;
@ -8,7 +9,7 @@ function animate(node, show, done) {
return cssAnimation(node, 'ant-motion-collapse-legacy', {
start() {
if (appearRequestAnimationFrameId) {
cancelAnimationFrame(appearRequestAnimationFrameId);
cancelAnimationTimeout(appearRequestAnimationFrameId);
}
if (!show) {
node.style.height = `${node.offsetHeight}px`;
@ -18,7 +19,7 @@ function animate(node, show, done) {
// not get offsetHeight when appear
// set it into raf get correct offsetHeight
if (height === 0) {
appearRequestAnimationFrameId = requestAnimationFrame(() => {
appearRequestAnimationFrameId = requestAnimationTimeout(() => {
height = node.offsetHeight;
node.style.height = '0px';
node.style.opacity = '0';
@ -31,19 +32,19 @@ function animate(node, show, done) {
},
active() {
if (requestAnimationFrameId) {
cancelAnimationFrame(requestAnimationFrameId);
cancelAnimationTimeout(requestAnimationFrameId);
}
requestAnimationFrameId = requestAnimationFrame(() => {
requestAnimationFrameId = requestAnimationTimeout(() => {
node.style.height = `${show ? height : 0}px`;
node.style.opacity = show ? '1' : '0';
});
},
end() {
if (appearRequestAnimationFrameId) {
cancelAnimationFrame(appearRequestAnimationFrameId);
cancelAnimationTimeout(appearRequestAnimationFrameId);
}
if (requestAnimationFrameId) {
cancelAnimationFrame(requestAnimationFrameId);
cancelAnimationTimeout(requestAnimationFrameId);
}
node.style.height = '';
node.style.opacity = '';

View File

@ -1,13 +1,12 @@
interface RafMap {
[id: number]: number;
}
import getRequestAnimationFrame, { cancelRequestAnimationFrame } from './getRequestAnimationFrame';
let id = 0;
const ids: RafMap = {};
const oriRaf = getRequestAnimationFrame();
export type RafFrame = {
id: number;
};
// Support call raf with delay specified frame
export default function raf(callback: () => void, delayFrames = 1): number {
const myId: number = id++;
export default function raf(callback: () => void, delayFrames = 1): { id: number } {
let restFrames: number = delayFrames;
function internalCallback() {
@ -15,22 +14,20 @@ export default function raf(callback: () => void, delayFrames = 1): number {
if (restFrames <= 0) {
callback();
delete ids[myId];
} else {
ids[myId] = requestAnimationFrame(internalCallback);
frame.id = oriRaf(internalCallback);
}
}
ids[myId] = requestAnimationFrame(internalCallback);
const frame = {
id: oriRaf(internalCallback),
};
return myId;
return frame;
}
raf.cancel = function cancel(pid?: number) {
if (pid === undefined) return;
raf.cancel = function cancel(frame?: { id: number }) {
if (!frame) return;
cancelAnimationFrame(ids[pid]);
delete ids[pid];
cancelRequestAnimationFrame(frame.id);
};
raf.ids = ids; // export this for test usage

View File

@ -1,5 +1,8 @@
import type { RafFrame } from './raf';
import raf from './raf';
export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
let requestId: number | null;
let requestId: RafFrame;
const later = (args: any[]) => () => {
requestId = null;
@ -8,11 +11,11 @@ export default function throttleByAnimationFrame(fn: (...args: any[]) => void) {
const throttled = (...args: any[]) => {
if (requestId == null) {
requestId = requestAnimationFrame(later(args));
requestId = raf(later(args));
}
};
(throttled as any).cancel = () => cancelAnimationFrame(requestId!);
(throttled as any).cancel = () => raf.cancel(requestId!);
return throttled;
}

View File

@ -4,6 +4,7 @@ import { computed, defineComponent, onBeforeUnmount, ref, watch } from 'vue';
import type { MenuMode } from './interface';
import { 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';
@ -48,7 +49,7 @@ export default defineComponent({
const popupPlacement = computed(() => popupPlacementMap[props.mode]);
const visibleRef = ref<number>();
const visibleRef = ref<RafFrame>();
watch(
() => props.visible,
visible => {

View File

@ -1,5 +1,6 @@
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({
@ -9,7 +10,7 @@ export default defineComponent({
setup(props, { attrs, slots }) {
const innerRef = ref<any>(null);
const rafRef = ref<number | null>(null);
const rafRef = ref<RafFrame>(null);
function cancelKeepAlign() {
raf.cancel(rafRef.value!);

View File

@ -1,3 +1,4 @@
import type { RafFrame } from '../../../_util/raf';
import raf from '../../../_util/raf';
import { onMounted, reactive, ref } from 'vue';
@ -5,7 +6,7 @@ type SetActionType<T> = Partial<T> | ((state: T) => Partial<T>);
export default function useFrameSetState<T extends object>(
initial: T,
): [Record<string, any>, (newState: SetActionType<T>) => void] {
const frame = ref(null);
const frame = ref<RafFrame>(null);
const state = reactive({ ...initial });
const queue = ref<SetActionType<T>[]>([]);

View File

@ -1,3 +1,5 @@
import type { RafFrame } from '../../_util/raf';
import raf from '../../_util/raf';
import type { ComputedRef, Ref, UnwrapRef } from 'vue';
import { ref, onBeforeUnmount, watch } from 'vue';
import type { ValueTextConfig } from './useValueTexts';
@ -8,15 +10,15 @@ export default function useHoverValue<DateType>(
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>,
): [ComputedRef<string>, (date: DateType) => void, (immediately?: boolean) => void] {
const innerValue = ref<DateType>(null);
const raf = ref(null);
let rafId: RafFrame;
function setValue(val: DateType, immediately = false) {
cancelAnimationFrame(raf.value);
raf.cancel(rafId);
if (immediately) {
innerValue.value = val as UnwrapRef<DateType>;
return;
}
raf.value = requestAnimationFrame(() => {
rafId = raf(() => {
innerValue.value = val as UnwrapRef<DateType>;
});
}
@ -38,7 +40,7 @@ export default function useHoverValue<DateType>(
onLeave(true);
});
onBeforeUnmount(() => {
cancelAnimationFrame(raf.value);
raf.cancel(rafId);
});
return [firstText, onEnter, onLeave];

View File

@ -3,6 +3,7 @@ import { onBeforeUnmount, watchEffect, watch, ref, computed } from 'vue';
import type { FocusEventHandler } from '../../_util/EventInterface';
import KeyCode from '../../_util/KeyCode';
import { addGlobalMousedownEvent, getTargetFromEvent } from '../utils/uiUtil';
import raf from '../../_util/raf';
export default function usePickerInput({
open,
@ -161,7 +162,7 @@ export default function usePickerInput({
preventBlurRef.value = true;
// Always set back in case `onBlur` prevented by user
requestAnimationFrame(() => {
raf(() => {
preventBlurRef.value = false;
});
} else if (!focused.value || clickedOutside) {

View File

@ -1,14 +1,15 @@
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<HTMLElement, number>();
const scrollIds = new Map<HTMLElement, RafFrame>();
/** Trigger when element is visible in view */
export function waitElementReady(element: HTMLElement, callback: () => void): () => void {
let id: number;
let id: RafFrame;
function tryOrNextFrame() {
if (isVisible(element)) {
@ -30,14 +31,14 @@ export function waitElementReady(element: HTMLElement, callback: () => void): ()
/* eslint-disable no-param-reassign */
export function scrollTo(element: HTMLElement, to: number, duration: number) {
if (scrollIds.get(element)) {
cancelAnimationFrame(scrollIds.get(element)!);
raf.cancel(scrollIds.get(element)!);
}
// jump to target if duration zero
if (duration <= 0) {
scrollIds.set(
element,
requestAnimationFrame(() => {
raf(() => {
element.scrollTop = to;
}),
);
@ -49,7 +50,7 @@ export function scrollTo(element: HTMLElement, to: number, duration: number) {
scrollIds.set(
element,
requestAnimationFrame(() => {
raf(() => {
element.scrollTop += perTick;
if (element.scrollTop !== to) {
scrollTo(element, to, duration - 10);

View File

@ -1,5 +1,6 @@
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';
@ -73,7 +74,7 @@ export default defineComponent({
const instance = getCurrentInstance();
let baseWidth = 0;
const dragging = ref(false);
let rafId: number;
let rafId: RafFrame;
const updateWidth = (e: HandleEvent) => {
let pageX = 0;
if (e.touches) {

View File

@ -1,3 +1,4 @@
import type { RafFrame } from '../../_util/raf';
import raf from '../../_util/raf';
import type { Ref, UnwrapRef } from 'vue';
import { onBeforeUnmount, ref, shallowRef } from 'vue';
@ -8,7 +9,7 @@ export function useLayoutState<State>(
defaultState: State,
): [Ref<State>, (updater: Updater<State>) => void] {
const stateRef = shallowRef<State>(defaultState);
let rafId: number;
let rafId: RafFrame;
const updateBatchRef = shallowRef<Updater<State>[]>([]);
function setFrameState(updater: Updater<State>) {
updateBatchRef.value.push(updater);

View File

@ -1,5 +1,6 @@
import type { Ref } from 'vue';
import { nextTick, onBeforeUnmount, ref, watch, onMounted } from 'vue';
import type { RafFrame } from '../../_util/raf';
import raf from '../../_util/raf';
/**
@ -23,7 +24,7 @@ export default (
doMeasure: Func,
): [Ref<PopupStatus>, (callback?: () => void) => void] => {
const status = ref<PopupStatus>(null);
const rafRef = ref<number>();
const rafRef = ref<RafFrame>();
const destroyRef = ref(false);
function setStatus(nextStatus: PopupStatus) {
if (!destroyRef.value) {

View File

@ -1,4 +1,5 @@
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';
@ -15,7 +16,7 @@ export default function useFrameWheel(
onWheelDelta: (offset: number) => void,
): [(e: WheelEvent) => void, (e: FireFoxDOMMouseScrollEvent) => void] {
let offsetRef = 0;
let nextFrame: number | null | undefined = null;
let nextFrame: RafFrame = null;
// Firefox patch
let wheelValue = null;

View File

@ -1,5 +1,6 @@
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';
@ -13,7 +14,7 @@ export default function useScrollTo(
syncScrollTop: (newTop: number) => void,
triggerFlash: () => void,
) {
let scroll: number | null = null;
let scroll: RafFrame = null;
return (arg?: any) => {
// When not argument provided, we think dev may want to show the scrollbar

View File

@ -102,7 +102,6 @@
"@types/markdown-it": "^10.0.2",
"@types/node": "^14.0.0",
"@types/postcss-load-config": "^2.0.1",
"@types/raf": "^3.4.0",
"@typescript-eslint/eslint-plugin": "^4.1.0",
"@typescript-eslint/parser": "^4.1.0",
"@vitejs/plugin-vue": "^1.2.4",