Note: please keep in mind that this method may not work properly with nested routes.
When implementing this technique, you must enclose each route within a div element that has a specific class for animating the route. Make sure to set the key and path attributes to match the route path as shown below:
function wrapRoute(route: h.JSX.Element): h.JSX.Element {
return ( // the 'in' class handles the animation while the 'page' class ensures smooth transition between old and new routes
<div path={route.props.path} class="in page" key={route.props.path}>
{route}
</div>
);
}
Next, you need to define two reactive variables like so:
const [previousEl, setPreviousEl] = useState<ComponentChildren | null>(null);
const [outEl, setOutEl] = useState<JSX.Element | null>(null);
Subsequently, you should listen for the onChange
event triggered by the preact-router
. When detected, set the outEl
variable to a div containing previousEl
, complete with a class that animates the exit of the current route. Include an onAnimationEnd
listener to reset outEl
to null once the exit animation finishes. Lastly, update previousEl
to e.current.props.children
. Your router component handler will resemble the following:
<Router onChange={(e) => {
if (previousEl) {
setOutEl(
<div class="out page" key={e.previous} onAnimationEnd={() => setOutEl(null)}>
{previousEl}
</div>
);
}
if (e.current) {
setPreviousEl(e.current.props.children);
}
}}
>
{routes} // an array containing all wrapped routes.
</Router>
Lastly, create the necessary animations by referencing the example in app.sass
provided below.
Here's the detailed example:
app.tsx
// Code mentioned above included here...
app.sass
.page
position: absolute
top: 0
bottom: 0
left: 0
right: 0
.out
animation: out 200ms forwards
@keyframes out
0%
opacity: 1
transform: translateX(0)
100%
opacity: 0
transform: translateX(100vw)
.in
animation: in 200ms forwards
@keyframes in
0%
opacity: 0
transform: translateX(-100vw)
100%
opacity: 1
transform: translateX(0)