Reputation: 4615
In this code example, the exit transition works, but the enter transition does not. I'm curious to if I approach this the right way of if this is a bug with styled-components.
The goal is to animate to 50% off screen when exiting, and to animate from 100% off screen to on-screen on enter. I've set the opacity to 50% to be able to see both page divs at all times.
I'd prefer to use Transition instead of CSSTransition as I'd like to purely use styled-components instead of mixing in regular style sheets.
Code example: https://codesandbox.io/s/sweet-mcnulty-dbptv
The animation states when exiting are (see console in CodeSandbox):
NOTE: This (exit transition) works well.
The animation states when entering are (see console in CodeSandbox):
This (the enter transition) does not animate at all. The big difference between on-exit and on-enter is that on-exit the component is already mounted, while on-enter it is mounted just before the animation should happen. My working thesis is that styled-component is not committing both the CSS updates (step 1 and 2) to the DOM individually, and thus the browser does not trigger the transition. But I Don't know this, anyhow it only happens if you mount just before the transition.
Any ideas?
Full code example (same as https://codesandbox.io/s/sweet-mcnulty-dbptv):
import React from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
import { TransitionGroup, Transition } from "react-transition-group";
const Nav = styled.nav`
display: flex;
justify-content: space-around;
align-items: center;
height: 80px;
`;
const Page = styled.div`
background: white;
display: flex;
justify-content: center;
align-items: center;
will-change: transform;
position: fixed;
top: 80px;
bottom: 0;
left: 0;
right: 0;
background: ${props => (props.backgroundColor === "gray" ? "#eee" : "#efe")};
opacity: 0.5;
transform: ${props => {
// Details page is green
if (props.backgroundColor === "green") {
// console.log(new Date().getTime(), 'Details page (css): ', props.transitionState);
}
switch (props.transitionState) {
case "entering":
return "translateX(0)";
case "entered":
return "translateX(0)";
case "exiting":
return "translateX(-50%)";
case "exited":
return "translateX(100%)";
default:
throw new Error("This should never happen");
}
}};
transition: ${props => {
switch (props.transitionState) {
case "entering":
return "transform 1000ms cubic-bezier(0, 0, 0, 1)";
case "entered":
return "none";
case "exiting":
return "transform 1000ms cubic-bezier(0, 0, 0, 1)";
case "exited":
return "none";
default:
throw new Error("This should never happen");
}
}};
`;
const HomePage = props => {
return (
<Page backgroundColor="gray" transitionState={props.transitionState}>
Home page!
</Page>
);
};
const DetailsPage = props => {
console.log(new Date().getTime(), 'Details page (component):', props.transitionState);
return (
<Page backgroundColor="green" transitionState={props.transitionState}>
Details page!
</Page>
);
};
function App() {
return (
<Router>
<Nav>
<Link to="/">Home</Link>
<Link to="/details">Details</Link>
</Nav>
<Route
render={({ location }) => {
return (
<TransitionGroup component={null}>
<Transition key={location.pathname} timeout={1000}>
{transitionState => {
return (
<Switch location={location}>
<Route
exact
path="/"
render={() => (
<HomePage transitionState={transitionState} />
)}
/>
<Route
exact
path="/details"
render={() => (
<DetailsPage transitionState={transitionState} />
)}
/>
</Switch>
);
}}
</Transition>
</TransitionGroup>
);
}}
/>
</Router>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Upvotes: 1
Views: 1247
Reputation: 4615
Here is a way around it, but it requires using CSSTransition. It still does not explain why the example in the original question does not work.
https://codesandbox.io/s/page-transition-with-csstransition-working-emudw
import React from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
import { BrowserRouter as Router, Route, Switch, Link } from "react-router-dom";
import { TransitionGroup, CSSTransition } from "react-transition-group";
const Nav = styled.nav`
display: flex;
justify-content: space-around;
align-items: center;
height: 80px;
`;
const Page = styled.div`
background: white;
display: flex;
justify-content: center;
align-items: center;
will-change: transform;
position: fixed;
top: 80px;
bottom: 0;
left: 0;
right: 0;
background: ${props => (props.backgroundColor === "gray" ? "#eee" : "#efe")};
opacity: 0.5;
&.page-transition-enter {
transform: translateX(100%);
transition: none;
}
&.page-transition-enter-active {
transform: translateX(0);
transition: transform 1000ms cubic-bezier(0, 0, 0, 1);
}
&.page-transition-exit {
transform: translateX(0);
transition: none;
}
&.page-transition-exit-active {
transform: translateX(-50%);
transition: transform 1000ms cubic-bezier(0, 0, 0, 1);
}
`;
const HomePage = props => {
return <Page backgroundColor="gray">Home page!</Page>;
};
const DetailsPage = props => {
return <Page backgroundColor="green">Details page!</Page>;
};
function App() {
return (
<Router>
<Nav>
<Link to="/">Home</Link>
<Link to="/details">Details</Link>
</Nav>
<Route
render={({ location }) => {
return (
<TransitionGroup component={null}>
<CSSTransition
key={location.pathname}
timeout={1000}
classNames="page-transition"
>
<Switch location={location}>
<Route exact path="/" render={() => <HomePage />} />
<Route exact path="/details" render={() => <DetailsPage />} />
</Switch>
</CSSTransition>
</TransitionGroup>
);
}}
/>
</Router>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Upvotes: 0