refactor(PageTransition): optimize layer management and transition handling

This commit is contained in:
LTbinglingfeng
2026-01-29 03:10:04 +08:00
parent 8148851a06
commit a88078e171
2 changed files with 50 additions and 60 deletions

View File

@@ -14,6 +14,7 @@
gap: $spacing-lg; gap: $spacing-lg;
min-height: 0; min-height: 0;
flex: 1; flex: 1;
background: var(--bg-secondary);
backface-visibility: hidden; backface-visibility: hidden;
transform: translateZ(0); transform: translateZ(0);
@@ -21,7 +22,6 @@
&--exit { &--exit {
position: absolute; position: absolute;
inset: 0; inset: 0;
z-index: 1;
overflow: hidden; overflow: hidden;
pointer-events: none; pointer-events: none;
will-change: transform, opacity; will-change: transform, opacity;
@@ -38,6 +38,5 @@
&--animating &__layer:not(.page-transition__layer--exit) { &--animating &__layer:not(.page-transition__layer--exit) {
position: relative; position: relative;
z-index: 0;
} }
} }

View File

@@ -1,4 +1,4 @@
import { ReactNode, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { ReactNode, useCallback, useLayoutEffect, useRef, useState } from 'react';
import { useLocation, type Location } from 'react-router-dom'; import { useLocation, type Location } from 'react-router-dom';
import gsap from 'gsap'; import gsap from 'gsap';
import './PageTransition.scss'; import './PageTransition.scss';
@@ -64,7 +64,7 @@ export function PageTransition({
return document.scrollingElement as HTMLElement | null; return document.scrollingElement as HTMLElement | null;
}, [scrollContainerRef]); }, [scrollContainerRef]);
useEffect(() => { useLayoutEffect(() => {
if (isAnimating) return; if (isAnimating) return;
if (location.key === currentLayerKey) return; if (location.key === currentLayerKey) return;
if (currentLayerPathname === location.pathname) return; if (currentLayerPathname === location.pathname) return;
@@ -89,9 +89,6 @@ export function PageTransition({
? getTransitionVariant(currentLayerPathname ?? '', location.pathname) ? getTransitionVariant(currentLayerPathname ?? '', location.pathname)
: 'vertical'; : 'vertical';
let cancelled = false;
queueMicrotask(() => {
if (cancelled) return;
setLayers((prev) => { setLayers((prev) => {
const variant = transitionVariantRef.current; const variant = transitionVariantRef.current;
const direction = transitionDirectionRef.current; const direction = transitionDirectionRef.current;
@@ -121,7 +118,9 @@ export function PageTransition({
const targetIndex = prev.findIndex((layer) => layer.key === location.key); const targetIndex = prev.findIndex((layer) => layer.key === location.key);
if (targetIndex !== -1) { if (targetIndex !== -1) {
const targetStack: Layer[] = prev.slice(0, targetIndex + 1).map((layer, idx): Layer => { const targetStack: Layer[] = prev
.slice(0, targetIndex + 1)
.map((layer, idx): Layer => {
const isTarget = idx === targetIndex; const isTarget = idx === targetIndex;
return { return {
...layer, ...layer,
@@ -143,11 +142,6 @@ export function PageTransition({
return [exitingLayer, nextCurrent]; return [exitingLayer, nextCurrent];
}); });
setIsAnimating(true); setIsAnimating(true);
});
return () => {
cancelled = true;
};
}, [ }, [
isAnimating, isAnimating,
location, location,
@@ -186,6 +180,10 @@ export function PageTransition({
nextLayersRef.current = null; nextLayersRef.current = null;
setLayers((prev) => nextLayers ?? prev.filter((layer) => layer.status !== 'exiting')); setLayers((prev) => nextLayers ?? prev.filter((layer) => layer.status !== 'exiting'));
setIsAnimating(false); setIsAnimating(false);
if (currentLayerEl) {
gsap.set(currentLayerEl, { clearProps: 'transform,opacity,boxShadow' });
}
}, },
}); });
@@ -202,14 +200,12 @@ export function PageTransition({
y: exitBaseY, y: exitBaseY,
xPercent: 0, xPercent: 0,
opacity: 1, opacity: 1,
zIndex: isForward ? 0 : 1,
}); });
} }
gsap.set(currentLayerEl, { gsap.set(currentLayerEl, {
xPercent: enterFromXPercent, xPercent: enterFromXPercent,
opacity: 1, opacity: 1,
zIndex: isForward ? 1 : 0,
}); });
const shadowValue = '-14px 0 24px rgba(0, 0, 0, 0.16)'; const shadowValue = '-14px 0 24px rgba(0, 0, 0, 0.16)';
@@ -241,14 +237,6 @@ export function PageTransition({
duration: IOS_TRANSITION_DURATION, duration: IOS_TRANSITION_DURATION,
ease: 'power2.out', ease: 'power2.out',
force3D: true, force3D: true,
onComplete: () => {
if (currentLayerEl) {
gsap.set(currentLayerEl, { clearProps: 'transform,opacity,boxShadow,zIndex' });
}
if (exitingLayerEl) {
gsap.set(exitingLayerEl, { clearProps: 'transform,opacity,boxShadow,zIndex' });
}
},
}, },
0 0
); );
@@ -300,10 +288,13 @@ export function PageTransition({
{layers.map((layer) => ( {layers.map((layer) => (
<div <div
key={layer.key} key={layer.key}
className={`page-transition__layer${ className={[
layer.status === 'exiting' ? ' page-transition__layer--exit' : '' 'page-transition__layer',
}${layer.status === 'stacked' ? ' page-transition__layer--stacked' : '' layer.status === 'exiting' ? 'page-transition__layer--exit' : '',
}`} layer.status === 'stacked' ? 'page-transition__layer--stacked' : '',
]
.filter(Boolean)
.join(' ')}
ref={ ref={
layer.status === 'exiting' layer.status === 'exiting'
? exitingLayerRef ? exitingLayerRef