Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec380f9e16 |
@@ -6,6 +6,7 @@ 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",
|
||||||
@@ -37,10 +38,14 @@ 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 [direction, setDirection] = useState<"forward" | "backward" | "neutral">("neutral");
|
const [exitDirection, setExitDirection] = 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>>();
|
||||||
|
|
||||||
@@ -52,25 +57,33 @@ 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) {
|
||||||
setDirection("forward");
|
setExitDirection("forward");
|
||||||
} else if (prevIndex > nextIndex) {
|
} else if (prevIndex > nextIndex) {
|
||||||
setDirection("backward");
|
setExitDirection("backward");
|
||||||
} else {
|
} else {
|
||||||
setDirection("neutral");
|
setExitDirection("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");
|
||||||
}, EXIT_DURATION_MS);
|
}, duration);
|
||||||
return () => clearTimeout(timerRef.current);
|
return () => clearTimeout(timerRef.current);
|
||||||
}, [viewKey, children]);
|
}, [viewKey, children]);
|
||||||
|
|
||||||
const dirClass = direction === "forward" ? " is-forward" : direction === "backward" ? " is-backward" : "";
|
const dirClass = exitDirection === "forward" ? " is-forward" : exitDirection === "backward" ? " is-backward" : "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={phase === "exit" ? `page-transition-wrap page-motion--exit${dirClass}` : `page-transition-wrap${phase === "idle" && direction !== "neutral" ? ` page-motion--enter${dirClass}` : ""}`}>
|
<div className={phase === "exit" ? `page-transition-wrap page-motion--exit${dirClass}` : "page-transition-wrap"}>
|
||||||
{displayedChildren}
|
{displayedChildren}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,15 +16,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Directional page transitions */
|
/* Directional exit transitions only — entrance is handled by child's page-motion */
|
||||||
.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;
|
||||||
}
|
}
|
||||||
@@ -33,28 +25,6 @@
|
|||||||
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;
|
||||||
@@ -67,4 +37,4 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(16px);
|
transform: translateX(16px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user