dynamic page sliding animation with React Router v4 and react-transition-group v2

2k views Asked by At

I asked a question before about how to realize the page sliding animation: page sliding animation with React Router v4 and react-transition-group v2.

After that, I got a more difficult problem.

function getPathDepth(location) {
  let pathArr = (location || {}).pathname.split('/');
  pathArr = pathArr.filter(n => n !== '');
  return pathArr.length;
}

class Routers extends React.Component {
  constructor(props, context) {
    super(props, context);
    this.state = {
      prevDepth: getPathDepth(props.location)
    };
  }

  componentWillReceiveProps() {
    this.setState({ prevDepth: getPathDepth(this.props.location) });
  }

  render() {
    return (
      <Route render={({ location }) => {
        return (
          <TransitionGroup>
            <CSSTransition
              key={location.key}
              timeout={500}
              classNames={ getPathDepth(location) - this.state.prevDepth > 0 ? 'pageSliderLeft' : 'pageSliderRight' }
              classNames={classNames}
              mountOnEnter={true}
              unmountOnExit={true}
            >
              <Switch location={location}>
                <Route path="/" exact component={ Index } />
                <Route path="/comments" component={ Comments } />
                <Route path="/opinions" component={ Opinions } />
                <Route path="/games/lol" component={ LOL } />
                <Route path="/games/dota" component={ DotA } />
                <Route path="/games" component={ Games } />
              </Switch>
            </CSSTransition>
          </TransitionGroup>
        );
      }} />
    )
  }
}

const WrapRouters = withRouter(Routers);

export default function RouterMap() {
  return (
    <BrowserRouter>
      <WrapRouters />
    </BrowserRouter>
  );
}

The CSS:

.pageSliderRight-enter {
  transform: translate3d(-100%, 0, 0);
}

.pageSliderRight-enter.pageSliderRight-enter-active {
  transform: translate3d(0, 0, 0);
  transition: all 600ms;
}
.pageSliderRight-exit {
  transform: translate3d(0, 0, 0);
}

.pageSliderRight-exit.pageSliderRight-exit-active {
  transform: translate3d(-100%, 0, 0);
  transition: all 600ms;
}

.pageSliderLeft-enter {
  transform: translate3d(100%, 0, 0);
}

.pageSliderLeft-enter.pageSliderLeft-enter-active {
  transform: translate3d(0, 0, 0);
  transition: all 600ms;
}
.pageSliderLeft-exit {
  transform: translate3d(0, 0, 0);
}

.pageSliderLeft-exit.pageSliderLeft-exit-active {
  transform: translate3d(100%, 0, 0);
  transition: all 600ms;
}

The animation:

enter image description here

If only sliding from '/' to '/games' and then from '/games' back to '/', everything is fine. But with more routes, it gets complicated.

When from '/' to '/games', the Games's exit animation is specified, not changeable. But obviously, it should be dynamic.

  • '/' --> '/games' && '/games' --> '/', exit animation should be 'slide to right'
  • '/' --> '/games' && '/games' --> '/games/lol', exit animation should be 'slide to left'

More generally, when getting deeper route, the sliding animation should be 'slide to left'. Otherwise, the animation should be 'slide to right'.

How to set the animation dynamically?

1

There are 1 answers

0
MatijaG On

So looking further as I already mentioned in the comment, I think the issue is that the exiting element has outdated state. To solve that I think that you would have to use the child factory and clone the element with new props before it exits.

Alternatively one could simplify the animation a bit (and it should also have better performance on mobile) by doing only the enter animation, and ignore the exit animation. Think of it like a stack of cards.

enter image description here

Something like this: CSS:

.pageSlider-exit  > .page{
    z-index:1;
}
.pageSlider-enter > .page{
    z-index:10;
}
.pageSlider-enter.left > .page{
    transform: translate3d(100%, 0, 0);
}

.pageSlider-enter.right  > .page{
    transform: translate3d(-100%, 0, 0);
}

.pageSlider-enter.pageSlider-enter-active  > .page{
    transform: translate3d(0, 0, 0);
    transition: all 600ms;
}

Full example: https://codepen.io/anon/pen/yoGqxQ