[Perf] useHeavyAnimationCheck: Support multiple heavy animations

This commit is contained in:
Alexander Zinchuk 2024-09-06 15:42:39 +02:00
parent ec6a28baf6
commit 79fca8ea1b
3 changed files with 29 additions and 19 deletions

View File

@ -245,6 +245,7 @@ function Transition({
requestMutation(() => { requestMutation(() => {
if (activeKey !== currentKeyRef.current) { if (activeKey !== currentKeyRef.current) {
dispatchHeavyAnimationStop();
return; return;
} }
@ -472,6 +473,7 @@ function performSlideOptimized(
requestMutation(() => { requestMutation(() => {
if (activeKey !== currentKeyRef.current) { if (activeKey !== currentKeyRef.current) {
dispatchHeavyAnimationStop();
return; return;
} }

View File

@ -14,7 +14,7 @@ const AUTO_END_TIMEOUT = 1000;
const startCallbacks = createCallbackManager(); const startCallbacks = createCallbackManager();
const endCallbacks = createCallbackManager(); const endCallbacks = createCallbackManager();
let timeout: number | undefined; let counter = 0;
const [getIsAnimating, setIsAnimating] = createSignal(false); const [getIsAnimating, setIsAnimating] = createSignal(false);
@ -79,29 +79,31 @@ export function isHeavyAnimating() {
} }
export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) { export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) {
if (!getIsAnimating()) { counter++;
if (counter === 1) {
setIsAnimating(true); setIsAnimating(true);
startCallbacks.runCallbacks(); startCallbacks.runCallbacks();
} }
if (timeout) { const timeout = window.setTimeout(onEnd, duration);
clearTimeout(timeout);
timeout = undefined; let hasEnded = false;
}
// Race condition may happen if another `dispatchHeavyAnimationEvent` is called before `onEnd`
function onEnd() { function onEnd() {
if (timeout) { if (hasEnded) return;
clearTimeout(timeout); hasEnded = true;
timeout = undefined;
clearTimeout(timeout);
counter--;
if (counter === 0) {
setIsAnimating(false);
endCallbacks.runCallbacks();
} }
setIsAnimating(false);
endCallbacks.runCallbacks();
} }
timeout = window.setTimeout(onEnd, duration);
return onEnd; return onEnd;
} }

View File

@ -20,6 +20,7 @@ type Params = Parameters<typeof createMutateFunction>;
let isAnimating = false; let isAnimating = false;
let currentArgs: Parameters<typeof createMutateFunction> | undefined; let currentArgs: Parameters<typeof createMutateFunction> | undefined;
let onHeavyAnimationStop: NoneToVoidFunction | undefined;
export default function animateScroll(...args: Params | [...Params, boolean]) { export default function animateScroll(...args: Params | [...Params, boolean]) {
currentArgs = args.slice(0, 8) as Params; currentArgs = args.slice(0, 8) as Params;
@ -117,15 +118,18 @@ function createMutateFunction(
return; return;
} }
isAnimating = true;
const transition = absPath <= FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE ? shortTransition : longTransition; const transition = absPath <= FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE ? shortTransition : longTransition;
const duration = forceDuration || ( const duration = forceDuration || (
FAST_SMOOTH_MIN_DURATION FAST_SMOOTH_MIN_DURATION
+ (absPath / FAST_SMOOTH_MAX_DISTANCE) * (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION) + (absPath / FAST_SMOOTH_MAX_DISTANCE) * (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION)
); );
const startAt = Date.now(); const startAt = Date.now();
const onHeavyAnimationStop = dispatchHeavyAnimationEvent();
isAnimating = true;
const prevOnHeavyAnimationStop = onHeavyAnimationStop;
onHeavyAnimationStop = dispatchHeavyAnimationEvent();
prevOnHeavyAnimationStop?.();
animateSingle(() => { animateSingle(() => {
const t = Math.min((Date.now() - startAt) / duration, 1); const t = Math.min((Date.now() - startAt) / duration, 1);
@ -138,7 +142,9 @@ function createMutateFunction(
if (!isAnimating) { if (!isAnimating) {
currentArgs = undefined; currentArgs = undefined;
onHeavyAnimationStop();
onHeavyAnimationStop!();
onHeavyAnimationStop = undefined;
} }
return isAnimating; return isAnimating;