I'm trying to achieve callback-based route transitions using Next.js's framework and Greensock animation library (if applicable). For example when I start on the homepage and then navigate to /about, I want to be able to do something like:
HomepageComponent.transitionOut(() => router.push('/about'))
ideally by listening to the router like a sort of middleware or something before pushing state
Router.events.on('push', (newUrl) => { currentPage.transitionOut().then(() => router.push(newUrl)) });
The main problem is that I also have a WebGL app running in the background, decoupled from the React ecosystem (since it uses requestAnimationFrame). So the reason I want callback-based transitions is because I need to run them after the WebGL transitions are done.
I've looked into using React Transition Group and I've seen the docs for the Router object but neither seems to be callback-based. In other words, when I transition to a new page, the WebGL and the page transitions run at the same time. And I don't want to do a hacky solution like adding a delay for the page transitions so they happen after the WebGL ones.
This is what I have right now:
app.js
<TransitionGroup>
<Transition
timeout={{ enter: 2000, exit: 2000 }}
// unmountOnExit={true}
onEnter={(node) => {
gsap.fromTo(node, { opacity: 0 }, { opacity: 1, duration: 1 });
}}
onExit={(node) => {
gsap.to(node, { opacity: 0, duration: 1 });
}}
key={router.route}
>
<Component {...pageProps}></Component>
</Transition>
</TransitionGroup>
webgl portion
Router.events.on('routeChangeStart', (url) => {
// transition webGL elements
// ideally would transition webGL elements and then allow callback to transition out html elements
});
I've also tried using the eventemitter3 library to do something like:
// a tag element click handler
onClick(e, href) {
e.preventDefault();
this.transitionOut().then(() => { Emitter.emit('push', href); });
// then we listen to Emitter 'push' event and that's when we Router.push(href)
}
However this method ran into huge issues when using the back / forward buttons for navigating
Bit late on this but I was looking into this myself today. It's really easy to use Framer Motion for this but I also wanted to use GSAP / React Transition Group.
For Framer Motion I just wrapped the Next < Component > with a motion component:
<motion.div
key={router.asPath}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Component {...pageProps} />
</motion.div>
For GSAP / React Transition Group, not sure if this is the right way but it's working as intended for me (see comments):
const [state, setstate] = useState(router.asPath) // I set the current asPath as the state
useEffect(() => {
const handleStart = () => {
setstate(router.asPath) // then on a router change, I'm setting the state again
// other handleStart logic goes here
}
const handleStop = () => {
... // handleStop logic goes here
}
router.events.on("routeChangeStart", handleStart)
router.events.on("routeChangeComplete", handleStop)
router.events.on("routeChangeError", handleStop)
return () => {
router.events.off("routeChangeStart", handleStart)
router.events.off("routeChangeComplete", handleStop)
router.events.off("routeChangeError", handleStop)
}
}, [router])
<Transition
in={router.asPath !== state} // here I'm just checking if the state has changed, then triggering the animations
onEnter={enter => gsap.set(enter, { opacity: 0 })}
onEntered={entered => gsap.to(entered, { opacity: 1, duration: 0.3 })}
onExit={exit => gsap.to(exit, { opacity: 0, duration: 0.3 })}
timeout={300}
appear
>
<Component {...pageProps} />
</Transition>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With