使用 React Router v4 和 react-transition-group v2 实现页面滑动动画 [英] page sliding animation with React Router v4 and react-transition-group v2

查看:64
本文介绍了使用 React Router v4 和 react-transition-group v2 实现页面滑动动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

const RouterMap = () =>(<路由器><路由渲染={({位置})=><过渡组><CSSTransition key={location.pathname.split('/')[1]} timeout={500} classNames="pageSlider" mountOnEnter={true} unmountOnExit={true}><切换位置={位置}><Route path="/" 精确组件={索引}/><Route path="/comments" component={ Comments }/><Route path="/opinions" component={ Opinions }/><Route path="/games" component={ Games }/></开关></CSSTransition></TransitionGroup>}/></路由器>)

和 CSS:

.pageSlider-enter {变换:translate3d(100%, 0, 0);}.pageSlider-enter.pageSlider-enter-active {变换:translate3d(0, 0, 0);过渡:全部 600 毫秒;}.pageSlider-exit {变换:translate3d(0, 0, 0);}.pageSlider-exit.pageSlider-exit-active {变换:translate3d(-100%, 0, 0);过渡:全部 600 毫秒;}

动画如下:

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

我知道如果我按如下方式更改 CSS,页面将从左到右显示:

.pageSlider-enter {变换:translate3d(-100%, 0, 0);}.pageSlider-exit.pageSlider-exit-active {变换:translate3d(100%, 0, 0);过渡:全部 600 毫秒;}

但是如何将两个动画结合在一起呢?一般来说,每当用户点击返回图标时,动画应该是从左到右.

<小时>

更新:2017.08.31

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

function getPathDepth(location) {让 pathArr = (location || {}).pathname.split('/');pathArr = pathArr.filter(n => n !== '');返回路径Arr.length;}<路由渲染={({位置})=><过渡组><CSS过渡key={location.pathname.split('/')[1]}超时={500}classNames={ getPathDepth(location) - this.state.prevDepth >0 ?'pageSliderLeft' : 'pageSliderRight' }mountOnEnter={真}卸载退出={真}><切换位置={位置}><Route path="/" 精确组件={索引}/><Route path="/comments" component={ Comments }/><Route path="/opinions" component={ Opinions }/><Route path="/games/lol" component={ LOL }/>//添加一条新路由<Route path="/games" component={ Games }/></开关></CSSTransition></TransitionGroup>}/>

并更新了 CSS:

.pageSliderLeft-enter {变换:translate3d(100%, 0, 0);}.pageSliderLeft-enter.pageSliderLeft-enter-active {变换:translate3d(0, 0, 0);过渡:全部 600 毫秒;}.pageSliderLeft-exit {变换:translate3d(0, 0, 0);}.pageSliderLeft-exit.pageSliderLeft-exit-active {变换:translate3d(100%, 0, 0);过渡:全部 600 毫秒;}.pageSliderRight-enter {变换:translate3d(-100%, 0, 0);}.pageSliderRight-enter.pageSliderRight-enter-active {变换:translate3d(0, 0, 0);过渡:全部 600 毫秒;}.pageSliderRight-exit {变换:translate3d(0, 0, 0);}.pageSliderRight-exit.pageSliderRight-exit-active {变换:translate3d(-100%, 0, 0);过渡:全部 600 毫秒;}

动画:

从'/'到'/games'没问题,从'/games'回到'/'还是可以的(类型1:路线A ->路线B,只有2条路线).但是如果先从'/'到'/games',然后从'/games'到'/games/lol',第二阶段失去动画(类型2:路线A ->路线B ->路线C,3 条或更多路线).我们也看到,从'/games/lol'回到'/games'再回到'/',滑动动画和type 1不一样.

有人知道这个问题吗?

解决方案

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

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

另一个问题是路由器只给你你要去的位置,所以你应该保存以前的位置(或深度),以便你可以比较.

之后就是在css类名之间切换的问题,类似的东西:

从'common/react'导入React从反应路由器"导入 { withRouter, Route, Switch }函数 getPathDepth(位置){return (location || {} ).pathname.split('/').length}类 RouterMap 扩展了 React.PureComponent {构造函数(道具,上下文){超级(道具,上下文)this.state = {prevDepth: getPathDepth(props.location),}}componentWillReceiveProps(){this.setState({ prevDepth: getPathDepth(this.props.location) })}使成为 () {返回 (<路由器><路由渲染={({位置})=>(<TransitionGroup><CSS过渡key={ location.pathname.split('/')[1] }超时={500}classNames={ getPathDepth(location) - this.state.prevDepth ?'pageSliderLeft' : 'pageSliderRight' } mountOnEnter={ true } unmountOnExit={ true }><切换位置={位置}><Route path='/'精确组件={索引}/><Route path='/comments' component={ Comments }/><Route path='/opinions' component={ Opinions }/><Route path='/games' component={ Games }/></开关></CSSTransition></TransitionGroup>)}/></路由器>)}}导出默认 withRouter(RouterMap)

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

I'm using React Router v4 and react-transition-group v2 to test the page sliding animation.

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>
)

And the 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;
}

The animation is as bellow:

As you see, the animation that index page slide to the detail page is all right(right to left). But when I click the Back icon, I hope index page comes out from left to right.

I know if I change the CSS as bellow, the page will come out from left to right:

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

But how combine the two animations together? Generally speaking, whenever user clicks the back icon, the animation should be from left to right.


Update: 2017.08.31

Thanks for @MatijaG, using the path depth is really an awesome idea. I followed it and got a new problem.

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>
} />

And updated 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;
}

The animation:

From '/' to '/games' is ok, and from '/games' back to '/' is still ok(type 1: route A -> route B, only 2 routes). But if firstly from '/' to '/games', and then from '/games' to '/games/lol', the second phase lose the animation(type 2: route A -> route B -> route C, 3 or more routes). We also see that from '/games/lol' back to '/games' and then back to '/', the slide animation is not same as type 1.

Anyone has any idea about this problem?

解决方案

Well the main problem is that even in your question you did not specify how should the app know which direction to use.

One way you could do it is by using the path depth/length: if the path you are navigating to is "deeper" than the current path, transition right to left, but if its shallower transition from left right?

The other problem is that router only gives you the location you are going to, so you should probably save the previous location (or depth) so you can compare it to.

After that is just a matter of switching between css classnames, something along this lines:

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)

Codepen example: https://codepen.io/matijag/pen/ayXpGr?editors=0110

这篇关于使用 React Router v4 和 react-transition-group v2 实现页面滑动动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆