I was wondering if anyone could provide some insight about how to achieve an animated component/page switch with React.js.
What i'd like to achieve is a component/page transition like the one on http://www.semplicelabs.com/ - a header that transitions opacity and margin-top and a content that transitions opacity.
When a new component/page is shown, the currently shown component/page should transition out before the new component/page transitioning in. What i have so far is a LayoutComponent
that renders a page component into a CSSTransitionGroup
:
import React from 'react/addons';
export default class LayoutComponent extends React.Component {
constructor(props) {
super(props);
this.state = { page: '' };
},
setPage(page) {
this.setState({ page });
}
render() {
return (
<React.addons.CSSTransitionGroup transitionName='page'>
{this.state.page}
</React.addons.CSSTransitionGroup>
);
}
}
I also have two page components, FirstPage
and SecondPage
.
import React from 'react';
export default class FirstPage extends React.Component {
render() {
return (
<div>
<header className='header'>
<div className='container'>
First Header
</div>
</header>
<div className='content'>
<div className='container'>
First Content
</div>
</div>
</div>
);
}
}
The only differences between SecondPage
code and FirstPage
code are the contents in the .container
divs and the classes name.
What i then tried to do is add a leave transition with .8s
duration and an enter transition with both .8s
duration and delay. The main problem i see is that the new page component is mounted before the leave transition of the old one has finished. This is my current CSS:
.page-enter {
background-color: #f2f2f2;
transition: background-color .8s linear .8s;
}
.page-enter-active {
background-color: #f2f1f1;
}
.page-leave {
background-color: #f2f1f1;
transition: background-color .8s linear;
}
.page-leave-active {
background-color: #f2f2f2;
}
.page-enter .header {
margin-top: -80px;
opacity: 0;
transition: opacity .8s ease-in-out .8s;
transition: margin-top .8s ease-in-out .8s;
}
.page-enter-active .header {
margin-top: 0;
opacity: 1;
}
.page-leave .header {
margin-top: 0;
opacity: 1;
transition: opacity .8s ease-in-out;
transition: margin-top .8s ease-in-out;
}
.page-leave-active .header {
margin-top: -80px;
opacity: 0;
}
.page-enter .content {
opacity: 0;
transition: opacity .8s ease-in-out .8s;
}
.page-enter-active .content {
opacity: 1;
}
.page-leave .content {
opacity: 1;
transition: opacity .8s ease-in-out;
}
.page-leave-active .content {
opacity: 0;
}
I know i could animate the initial mounting with transitionAppear={true}
- but that doesn't help with the problem of the new component being mounted before the old one is transitioned out.
This is a problem I have struggled with for a few days and I can't find a solution for.
Some code to play around with: https://jsfiddle.net/gonsfx/xva2g6oo/
i have added a state to the pages to save a opacity
(for the animation) and set it to 1 (initial = 0) when the component did mount with a small delay. So i added two functions to each page.
componentDidMount() {
setTimeout(() => {
this.setState({
opacity: 1
});
}, 20);
}
getStyle() {
return { opacity: this.state.opacity };
}
Then I added a constructor to make initial stuff....
constructor(props) {
super(props);
this.state = { opacity: 0 };
this.getStyle = this.getStyle.bind(this);
this.componentDidMount = this.componentDidMount.bind(this);
}
Here is a DEMO.
The components will appear immediately on the page, so i think that it isn't possible to make a fade out animation (At least, I did not found a solution for that).
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