使用React Router v4和react-transition-group v2进行页面滑动动画

jas*_*son 5 react-router react-transition-group

我正在使用React Router v4和react-transition-group v2来测试页面滑动动画.

const RouterMap = () => (
  <Router>
    <Route render={({ location }) =>
      <TransitionGroup>
        <CSSTransition key={location.pathname.split('/')[1]} timeout={500} classNames="pageSlider" 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" component={ Games } />
          </Switch>
        </CSSTransition>
      </TransitionGroup>
    } />
  </Router>
)
Run Code Online (Sandbox Code Playgroud)

而CSS:

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

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

.pageSlider-exit.pageSlider-exit-active {
  transform: translate3d(-100%, 0, 0);
  transition: all 600ms;
}
Run Code Online (Sandbox Code Playgroud)

动画如下:

在此输入图像描述

如您所见,index page滑动到的动画detail page是正确的(从右到左).但是当我点击Back图标时,我希望index page从左到右出来.

我知道如果我将CSS改为下面,页面将从左到右出现:

.pageSlider-enter {
  transform: translate3d(-100%, 0, 0);
}
.pageSlider-exit.pageSlider-exit-active {
  transform: translate3d(100%, 0, 0);
  transition: all 600ms;
}
Run Code Online (Sandbox Code Playgroud)

但是如何将两个动画结合在一起呢?一般来说,每当用户点击后退图标时,动画应该是from left to right.


更新:2017.08.31

感谢@MatijaG,使用路径深度真的是一个很棒的主意.我跟着它,遇到了一个新问题.

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

<Route render={({ location }) =>
  <TransitionGroup>
    <CSSTransition
      key={location.pathname.split('/')[1]}
      timeout={500}
      classNames={ getPathDepth(location) - this.state.prevDepth > 0 ? 'pageSliderLeft' : 'pageSliderRight' }
      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 } /> // add a new route
        <Route path="/games" component={ Games } />
      </Switch>
    </CSSTransition>
  </TransitionGroup>
} />
Run Code Online (Sandbox Code Playgroud)

更新的CSS:

.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;
}

.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;
}
Run Code Online (Sandbox Code Playgroud)

动画:

在此输入图像描述

从'/'到'/ games'是好的,从'/ games'回到'/'仍然可以(类型1:路线A - >路线B,只有2条路线).但是如果首先从'/'到'/ games',然后从'/ games'到'/ games/lol',第二阶段将失去动画(类型2:路线A - >路线B - >路线C,3或更多路线).我们还看到从'/ games/lol'回到'/ games'然后再回到'/',幻灯片动画与类型1不同.

有人对这个问题有任何想法吗?

Mat*_*jaG 8

那么主要的问题是,即使在您的问题中,您也未指定应用程序应该如何知道使用哪个方向.

一种方法是使用路径深度/长度:如果您导航到的路径比当前路径"更深",则从右向左过渡,但如果从左侧向右过渡?

另一个问题是路由器只为您提供了您要去的位置,因此您应该保存以前的位置(或深度),以便将其与之进行比较.

之后只是在css类名之间切换的问题,就是这样的:

import React from 'common/react'
import { withRouter, Route, Switch } from 'react-router'

function getPathDepth (location) {
    return (location || {} ).pathname.split('/').length
}
class RouterMap extends React.PureComponent {
    constructor (props, context) {
        super(props, context)
        this.state = {
            prevDepth: getPathDepth(props.location),
        }
    }

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

        return (
            <Router>
                <Route render={ ({ location }) =>
                    (<TransitionGroup>
                        <CSSTransition
                            key={ location.pathname.split('/')[1] }
                            timeout={ 500 }
                            classNames={ getPathDepth(location) - this.state.prevDepth ? 'pageSliderLeft' : 'pageSliderRight' } 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' component={ Games } />
                            </Switch>
                        </CSSTransition>
                    </TransitionGroup>)
                }
                />
            </Router>
        )
    }
}

export default withRouter(RouterMap)
Run Code Online (Sandbox Code Playgroud)

Codepen示例:https://codepen.io/matijag/pen/ayXpGr edit = 0110