I am using react-transition-group in a ReactJS and GatsbyJS (V2) project.
I have my page transitions working with animation but when Link
exit, the exiting
animation is cut short because the next page is ready for entering.
I have tried delaying the Link
action but whilst the page change was delayed, the exit
animation is not triggered until the delay
was over and the Link
was actioned.
How can I delay
the page change, whilst initiating the exiting
animation onClick
? Alternatively, is there a better way or props
available?
Here is my code
Layout.js
class Layout extends React.Component {
...
return (
<Transition>{children}</Transition>
);
}
Transition.js
class Transition extends React.Component {
constructor(props) {
super(props);
this.state = { exiting: false };
this.listenerHandler = this.listenerHandler.bind(this);
}
listenerHandler() {
this.setState({ exiting: true });
}
componentDidMount() {
window.addEventListener(historyExitingEventType, this.listenerHandler);
}
componentWillUnmount() {
window.removeEventListener(historyExitingEventType, this.listenerHandler);
}
static getDerivedStateFromProps({ exiting }) {
if (exiting) {
return { exiting: false };
}
return null;
}
render() {
const transitionProps = {
timeout: {
enter: 0,
exit: timeout
},
appear: true,
in: !this.state.exiting
};
return (
<ReactTransition {...transitionProps}>
{status => (
<div
style={{
...getTransitionStyle({ status, timeout })
}}
>
{this.props.children}
</div>
)}
</ReactTransition>
);
}
}
export default Transition;
gatsby-config.js
import createHistory from 'history/createBrowserHistory';
const timeout = 1500;
const historyExitingEventType = `history::exiting`;
const getUserConfirmation = (pathname, callback) => {
const event = new CustomEvent(historyExitingEventType, {
detail: { pathname }
});
window.dispatchEvent(event);
setTimeout(() => {
callback(true);
}, timeout);
};
let history;
if (typeof document !== 'undefined') {
history = createHistory({ getUserConfirmation });
history.block(location => location.pathname);
}
export const replaceHistory = () => history;
export { historyExitingEventType, timeout };
getTransitionStyle.js
const getTransitionStyles = timeout => {
return {
entering: {
transform: `scale(1.05) translateZ(0)`,
opacity: 0
},
entered: {
transition: `transform 750ms ease, opacity ${timeout}ms ease`,
transitionDelay: `750ms`,
transform: `scale(1) translateZ(0)`,
opacity: 1
},
exiting: {
transition: `transform 750ms ease, opacity ${timeout}ms ease`,
transform: `scale(0.98) translateZ(0)`,
opacity: 0
}
};
};
const getTransitionStyle = ({ timeout, status }) =>
getTransitionStyles(timeout)[status];
export default getTransitionStyle;
CSS Demo: transition-delayA value of 0s (or 0ms ) will begin the transition effect immediately. A positive value will delay the start of the transition effect for the given length of time. A negative value will begin the transition effect immediately, and partway through the effect.
unmountOnExit. By default the child component stays mounted after it reaches the 'exited' state. Set unmountOnExit if you'd prefer to unmount the component after it finishes exiting. type: boolean. default: false.
React Transition Group is not an animation library like React-Motion, it does not animate styles by itself. Instead it exposes transition stages, manages classes and group elements and manipulates the DOM in useful ways, making the implementation of actual visual transitions much easier.
The trick for stagger animation is by delaying the animation by a certain amount gradually. For this gradual delay, we can use the item position inside the array as a delay metric. Hence, animation-delay becomes a multiple of array index for a given item with 100ms as per above example. And That's it!
Gatsby v2 is using Reach Router instead of React Router, so using getUserConfirmation
with replaceHistory
will not work anymore. In the Gatsby v2 RC you can use react-pose
to create page transitions a little more straightforward:
gatsby-browser.js and gatsby-ssr.js:
import React from "react"
import Transition from "./src/components/transition"
export const wrapPageElement = ({ element, props }) => {
return <Transition {...props}>{element}</Transition>
}
transition.js component:
import React from "react"
import posed, { PoseGroup } from "react-pose"
const timeout = 250
class Transition extends React.PureComponent {
render() {
const { children, location } = this.props
const RoutesContainer = posed.div({
enter: { delay: timeout, delayChildren: timeout },
})
// To enable page transitions on mount / initial load,
// use the prop `animateOnMount={true}` on `PoseGroup`.
return (
<PoseGroup>
<RoutesContainer key={location.pathname}>{children}</RoutesContainer>
</PoseGroup>
)
}
}
export default Transition
Inside your pages:
// Use `posed.div` elements anywhere on the pages.
const Transition = posed.div({
enter: {
opacity: 1,
},
exit: {
opacity: 0,
},
})
// ...
<Transition>Hello World!</Transition>
Check the official example for a working demo.
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