Compare commits

..

1 Commits

2 changed files with 39 additions and 22 deletions
+7 -20
View File
@@ -6,7 +6,6 @@ interface PageTransitionProps {
} }
const EXIT_DURATION_MS = 180; const EXIT_DURATION_MS = 180;
const REDUCED_MOTION_EXIT_MS = 0;
const NAV_ORDER: string[] = [ const NAV_ORDER: string[] = [
"home", "home",
@@ -38,14 +37,10 @@ function getNavIndex(key: string): number {
return NAV_ORDER.indexOf(key); return NAV_ORDER.indexOf(key);
} }
const prefersReducedMotion = typeof window !== "undefined"
? window.matchMedia("(prefers-reduced-motion: reduce)").matches
: false;
export default function PageTransition({ viewKey, children }: PageTransitionProps) { export default function PageTransition({ viewKey, children }: PageTransitionProps) {
const [displayedChildren, setDisplayedChildren] = useState(children); const [displayedChildren, setDisplayedChildren] = useState(children);
const [phase, setPhase] = useState<"idle" | "exit">("idle"); const [phase, setPhase] = useState<"idle" | "exit">("idle");
const [exitDirection, setExitDirection] = useState<"forward" | "backward" | "neutral">("neutral"); const [direction, setDirection] = useState<"forward" | "backward" | "neutral">("neutral");
const prevKeyRef = useRef(viewKey); const prevKeyRef = useRef(viewKey);
const timerRef = useRef<ReturnType<typeof setTimeout>>(); const timerRef = useRef<ReturnType<typeof setTimeout>>();
@@ -57,33 +52,25 @@ export default function PageTransition({ viewKey, children }: PageTransitionProp
const prevIndex = getNavIndex(prevKeyRef.current); const prevIndex = getNavIndex(prevKeyRef.current);
const nextIndex = getNavIndex(viewKey); const nextIndex = getNavIndex(viewKey);
if (prevIndex < nextIndex) { if (prevIndex < nextIndex) {
setExitDirection("forward"); setDirection("forward");
} else if (prevIndex > nextIndex) { } else if (prevIndex > nextIndex) {
setExitDirection("backward"); setDirection("backward");
} else { } else {
setExitDirection("neutral"); setDirection("neutral");
} }
prevKeyRef.current = viewKey; prevKeyRef.current = viewKey;
if (prefersReducedMotion) {
setDisplayedChildren(children);
setPhase("idle");
return;
}
setPhase("exit"); setPhase("exit");
const duration = prefersReducedMotion ? REDUCED_MOTION_EXIT_MS : EXIT_DURATION_MS;
timerRef.current = setTimeout(() => { timerRef.current = setTimeout(() => {
setDisplayedChildren(children); setDisplayedChildren(children);
setPhase("idle"); setPhase("idle");
}, duration); }, EXIT_DURATION_MS);
return () => clearTimeout(timerRef.current); return () => clearTimeout(timerRef.current);
}, [viewKey, children]); }, [viewKey, children]);
const dirClass = exitDirection === "forward" ? " is-forward" : exitDirection === "backward" ? " is-backward" : ""; const dirClass = direction === "forward" ? " is-forward" : direction === "backward" ? " is-backward" : "";
return ( return (
<div className={phase === "exit" ? `page-transition-wrap page-motion--exit${dirClass}` : "page-transition-wrap"}> <div className={phase === "exit" ? `page-transition-wrap page-motion--exit${dirClass}` : `page-transition-wrap${phase === "idle" && direction !== "neutral" ? ` page-motion--enter${dirClass}` : ""}`}>
{displayedChildren} {displayedChildren}
</div> </div>
); );
+31 -1
View File
@@ -16,7 +16,15 @@
} }
} }
/* Directional exit transitions only — entrance is handled by child's page-motion */ /* Directional page transitions */
.page-motion--enter.is-forward {
animation: page-slide-in-forward 200ms var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)) both;
}
.page-motion--enter.is-backward {
animation: page-slide-in-backward 200ms var(--ease-out-expo, cubic-bezier(0.16, 1, 0.3, 1)) both;
}
.page-motion--exit.is-forward { .page-motion--exit.is-forward {
animation: page-slide-out-forward 180ms ease both; animation: page-slide-out-forward 180ms ease both;
} }
@@ -25,6 +33,28 @@
animation: page-slide-out-backward 180ms ease both; animation: page-slide-out-backward 180ms ease both;
} }
@keyframes page-slide-in-forward {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes page-slide-in-backward {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes page-slide-out-forward { @keyframes page-slide-out-forward {
to { to {
opacity: 0; opacity: 0;