美文网首页
react-router4路由切换动画

react-router4路由切换动画

作者: 我只是个NPC | 来源:发表于2018-01-20 23:01 被阅读1707次

写在前面

之前我写过关于react-transition-groupreact-motion的使用教程,就控制粒度上来说,react-motion要好很多,但是react-motion有个比较麻烦的问题,就是我暂时没找到开箱即用的动画封装,而用react-motion也没法使用animate.css,所以如果项目仅仅只用于web端,没考虑native,那么建议还是使用react-transition-group,会方便很多

用到的第三方库

  • react-router-dom 4.2.2 用于路由
  • react-transition-group 2.2.1 用于react动画实现,这里需要注意下,使用的不是版本1,而是只包含{Transition, TransitionGroup, CSSTransition}的版本2
  • animate.css 用于动画效果

热身

在正式开始写路由的切换动画前,我们先用react-transition-group结合animate.css来实现一个简单的组件进出场动画,以此回顾之前关于react-transition-group的知识
react-transition-group文档

    <div>
        <button onClick={this.toggleState}>click</button>
        {/*第一个例子*/}
        <CSSTransition
          in={this.state.show}
          classNames={{
            enter: 'animated',
            enterActive: 'bounceIn',
            exit: 'animated',
            exitActive: 'bounceOut'
          }}
          timeout={500}
          mountOnEnter={true}
          unmountOnExit={true}
        >
          <div className="box" />
        </CSSTransition>
      </div>

效果

demo1.gif
代码很简单,用in控制组件的显示和隐藏,用classNames控制组件进出场的className,稍微需要注意的是与animate.css的结合方式

路由切换

回顾了组件的进场和出场动画实现后,我们正式来开始写路由的切换动画。先理清楚思路,在react-router4里面,每个路由对应其实就是一个组件,无非就是在路由匹配到的时候,将CSSTransitionin设置为true,不匹配设置为false,仅此而已。
唯一麻烦的地方在于怎么获取路由的匹配信息,翻看react-router4的api,我们看到,Route和渲染有关的props有三个,component,render,children,componentrender都拿不到匹配信息,只要路由匹配到,组件就会mount,反之,就会unmount,我们无法进行控制,而children正好符合我们的期望,它与render类似,不同的地方在于无论path是否匹配,都会被触发,然后会将当前的match信息传递过来,我们也正好可以通过match来控制CSSTransition

先写一个无动画的路由切换

不管怎么样,我们先写个简单的路由切换,然后再对其进行改装

主路由

<Router>
        <div className="router4-transition">
          <div className="nav">
            <NavLink to="/" exact className="nav-item" activeClassName="active">
              home
            </NavLink>
            <NavLink to="/page1" className="nav-item" activeClassName="active">
              page1
            </NavLink>
            <NavLink to="/page2" className="nav-item" activeClassName="active">
              page2
            </NavLink>
          </div>

          <div className="pages">
            <Route
              path="/"
              exact
              component={props => {
                if(!props.match) return null
                return <Index />
              }}
            />
            <Route
              path="/page1"
              children={props => {
                if(!props.match) return null
                return <Page1 />
              }}
            />
            <Route
              path="/page2"
              children={props => {
                if(!props.match) return null
                return <Page2 />
              }}
            />
          </div>
        </div>
      </Router>

Index

class Index extends Component {
    render() {
      return <div className="page index">index</div>
    }
  }

Page1

class Page1 extends Component {
    render() {
      return <div className="page page1">page1</div>
    }
  }

Page2

class Page2 extends Component {
    render() {
      return <div className="page page2">page2</div>
    }
  }

简单的路由就写好了,接下来考虑加动画

利用高阶组件给页面加上动画

我并不希望在页面内部实现动画逻辑,首先是页面逻辑与动画逻辑无关,其次如果每写一个页面就写一次动画逻辑,我怕是要累死,所以我们这里将动画逻辑单独抽取出来,封装成一个高阶组件

function wrapAnimation(WrappedComponent) {
  return class extends Component {
    render() {
      return (
        <CSSTransition
          in={this.props.match !== null}
          classNames={{
            enter: 'animated',
            enterActive: 'fadeInDown',
            exit: 'animated',
            exitActive: 'fadeOutDown'
          }}
          timeout={1000}
          mountOnEnter={true}
          unmountOnExit={true}
        >
          <WrappedComponent {...this.props} />
        </CSSTransition>
      )
    }
  }
}

也是没啥可讲的代码,接下来,我们将我们的页面Index, Page1, Page2外面套一层高阶组件

const Index = wrapAnimation(
  class Index extends Component {
    render() {
      return <div className="page index">index</div>
    }
  }
)

const Page1 = wrapAnimation(
  class Page1 extends Component {
    render() {
      return <div className="page page1">page1</div>
    }
  }
)

const Page2 = wrapAnimation(
  class Page2 extends Component {
    render() {
      return <div className="page page2">page2</div>
    }
  }
)

ok,然后再稍微修改下我们的路由层

<Router>
        <div className="router4-transition">
          <div className="nav">
            <NavLink to="/" exact className="nav-item" activeClassName="active">
              home
            </NavLink>
            <NavLink to="/page1" className="nav-item" activeClassName="active">
              page1
            </NavLink>
            <NavLink to="/page2" className="nav-item" activeClassName="active">
              page2
            </NavLink>
          </div>

          <div className="pages">
            <Route
              path="/"
              exact
              children={props => {
                return <Index {...props} />
              }}
            />
            <Route
              path="/page1"
              children={props => {
                return <Page1 {...props} />
              }}
            />
            <Route
              path="/page2"
              children={props => {
                return <Page2 {...props} />
              }}
            />
          </div>
        </div>
      </Router>

接下来,直接看效果吧


router4-transition.gif

总结

和普通的组件切换动画差不多,只是需要注意下怎么在react-router4的路由中获取组件的显示状态

完整的代码我放到了github上: https://github.com/soyal/router4-transition

感谢你的阅读

相关文章

网友评论

      本文标题:react-router4路由切换动画

      本文链接:https://www.haomeiwen.com/subject/qdaowxtx.html