diff --git a/src/components/ui/Transition.tsx b/src/components/ui/Transition.tsx index 3af7da941..57bd17407 100644 --- a/src/components/ui/Transition.tsx +++ b/src/components/ui/Transition.tsx @@ -245,6 +245,7 @@ function Transition({ requestMutation(() => { if (activeKey !== currentKeyRef.current) { + dispatchHeavyAnimationStop(); return; } @@ -472,6 +473,7 @@ function performSlideOptimized( requestMutation(() => { if (activeKey !== currentKeyRef.current) { + dispatchHeavyAnimationStop(); return; } diff --git a/src/hooks/useHeavyAnimationCheck.ts b/src/hooks/useHeavyAnimationCheck.ts index b7b97d5d7..5adc63419 100644 --- a/src/hooks/useHeavyAnimationCheck.ts +++ b/src/hooks/useHeavyAnimationCheck.ts @@ -14,7 +14,7 @@ const AUTO_END_TIMEOUT = 1000; const startCallbacks = createCallbackManager(); const endCallbacks = createCallbackManager(); -let timeout: number | undefined; +let counter = 0; const [getIsAnimating, setIsAnimating] = createSignal(false); @@ -79,29 +79,31 @@ export function isHeavyAnimating() { } export function dispatchHeavyAnimationEvent(duration = AUTO_END_TIMEOUT) { - if (!getIsAnimating()) { + counter++; + + if (counter === 1) { setIsAnimating(true); startCallbacks.runCallbacks(); } - if (timeout) { - clearTimeout(timeout); - timeout = undefined; - } + const timeout = window.setTimeout(onEnd, duration); + + let hasEnded = false; - // Race condition may happen if another `dispatchHeavyAnimationEvent` is called before `onEnd` function onEnd() { - if (timeout) { - clearTimeout(timeout); - timeout = undefined; + if (hasEnded) return; + hasEnded = true; + + clearTimeout(timeout); + + counter--; + + if (counter === 0) { + setIsAnimating(false); + endCallbacks.runCallbacks(); } - - setIsAnimating(false); - endCallbacks.runCallbacks(); } - timeout = window.setTimeout(onEnd, duration); - return onEnd; } diff --git a/src/util/animateScroll.ts b/src/util/animateScroll.ts index 8de840671..c50e1d7d8 100644 --- a/src/util/animateScroll.ts +++ b/src/util/animateScroll.ts @@ -20,6 +20,7 @@ type Params = Parameters; let isAnimating = false; let currentArgs: Parameters | undefined; +let onHeavyAnimationStop: NoneToVoidFunction | undefined; export default function animateScroll(...args: Params | [...Params, boolean]) { currentArgs = args.slice(0, 8) as Params; @@ -117,15 +118,18 @@ function createMutateFunction( return; } - isAnimating = true; - const transition = absPath <= FAST_SMOOTH_SHORT_TRANSITION_MAX_DISTANCE ? shortTransition : longTransition; const duration = forceDuration || ( FAST_SMOOTH_MIN_DURATION + (absPath / FAST_SMOOTH_MAX_DISTANCE) * (FAST_SMOOTH_MAX_DURATION - FAST_SMOOTH_MIN_DURATION) ); const startAt = Date.now(); - const onHeavyAnimationStop = dispatchHeavyAnimationEvent(); + + isAnimating = true; + + const prevOnHeavyAnimationStop = onHeavyAnimationStop; + onHeavyAnimationStop = dispatchHeavyAnimationEvent(); + prevOnHeavyAnimationStop?.(); animateSingle(() => { const t = Math.min((Date.now() - startAt) / duration, 1); @@ -138,7 +142,9 @@ function createMutateFunction( if (!isAnimating) { currentArgs = undefined; - onHeavyAnimationStop(); + + onHeavyAnimationStop!(); + onHeavyAnimationStop = undefined; } return isAnimating;