In my app, I’ve implemented a tab-styled navigation bar to facilitate navigation between different categories of places, each with its own route. I’ve successfully applied page transitions between these routes using the code below in a nested layout file.
However, I’m encountering two specific issues:
-
Transition Animation and Modals: When a user clicks on an item, it’s
navigated to a different route presented as a modal with the help of
Parallel Routes. I’m facing challenges with the page transition
animation still running behind the modal when it’s opened or closed.
While I’ve managed to prevent this partially, there’s still some
overlap occurring. -
Transition Direction based on Tab Position: Additionally, I want the
transition direction to be determined by the tab’s position in the
navigation bar. For example, if a user clicks on a tab to the right
of the current one, I’d like the animation to move from left to
right.
I attempted to utilize the useParams() hook for obtaining the current route. Then, with each change, I aimed to store the previous version using useRef(). If neither the previous nor the current path contains the place property, I intended to increment another counter, keyCounter, initialized with useRef(0), which serves as the key for the motion element. However, despite my attempts, I couldn’t get it to work as expected.
const FrozenRouter = (props: PropsWithChildren<{}>) => {
const context = useContext(LayoutRouterContext);
const frozen = useRef(context).current;
return <LayoutRouterContext.Provider value={frozen}>{props.children}</LayoutRouterContext.Provider>;
};
const Layout = ({ children }: { children: ReactNode }) => {
const params = useParams();
const paramsBeforeChange = useRef<Params>({});
const keyCounter = useRef(0);
useEffect(() => {
const { city: cityBeforeChange, category: categoryBeforeChange, place: placeBeforeChange } = paramsBeforeChange.current;
const { city: cityAfterChange, category: categoryAfterChange, place: placeAfterChange } = params;
if (placeBeforeChange !== undefined || placeAfterChange !== undefined) {
console.log("shouldn't animate");
return;
}
keyCounter.current++;
paramsBeforeChange.current = params;
}, [params]);
return (
<>
<Navbar />
<AnimatePresence mode="popLayout" initial={false}>
<motion.main
key={keyCounter.current}
className="h-screen overflow-y-scroll pb-8 pt-28"
initial={{ opacity: 1, x: "-100%" }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 1, x: "100%" }}
transition={{ duration: 0.6, ease: "easeInOut" }}
>
<FrozenRouter>{children}</FrozenRouter>
</motion.main>
</AnimatePresence>
</>
);
};