问题描述
我正在尝试使用 Next.js 的框架和 Greensock 动画库(如果适用)实现基于回调的路由转换。例如,当我从主页开始然后导航到 /about
时,我希望能够执行以下操作:
HomepageComponent.transitionOut(() => router.push('/about'))
理想情况下,在推送状态之前通过像中间件之类的东西一样监听路由器
Router.events.on('push',(newUrl) => { currentPage.transitionOut().then(() => router.push(newUrl)) });
主要问题
主要问题是我还有一个在后台运行的 WebGL 应用程序,与 React 生态系统分离(因为它使用 requestAnimationFrame)。所以我想要基于回调的转换的原因是因为我需要在 WebGL 转换完成后运行它们。
当前实施
我研究过使用 React Transition Group 并且看到了 docs for the Router object,但似乎都不是基于回调的。换句话说,当我转换到一个新页面时,WebGL 和页面转换同时运行。而且我不想做一个笨拙的解决方案,比如为页面转换添加延迟,以便它们发生在 WebGL 转换之后。
这就是我现在所拥有的:
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 部分
Router.events.on('routeChangeStart',(url) => {
// transition webGL elements
// ideally would transition webGL elements and then allow callback to transition out html elements
});
我还尝试使用 eventemitter3 库来执行以下操作:
// 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)
}
然而,这种方法在使用后退/前进按钮进行导航时遇到了巨大的问题
解决方法
这有点晚了,但我今天自己也在研究这个。为此使用 Framer Motion 真的很容易,但我也想使用 GSAP/React Transition Group。
对于 Framer Motion,我只是用一个运动组件包裹了 Next :
<motion.div
key={router.asPath}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Component {...pageProps} />
</motion.div>
对于 GSAP / React Transition Group,不确定这是否是正确的方法,但它按我的预期工作(见评论):
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>