基于react-router实现的一个媲美原生路由动画效果的Router

在使用React开发web页面的时候,一般都会使用react-router来实现路由功能,相较于native路由流畅丝滑的体验,web页面切换起来会很生硬。作为从 iOS 转前端的我来说,尤其不能接受,因此,在 react-router 的基础上,写了一个 Router 库 react-router-virgo,让使用者只需要一行代码 + 一个路由配置文件就可以使 Web 页面切换能达到native路由切换的流畅体验。

react-router-virgo 简介

这个路由组件还没完全达到我的预期,后面有时间会继续迭代优化。目前这个路由组件支持以下功能:

  • 无转场动画切换(现有的 web 页面切换体验)
  • push 动画切换(右侧淡入,右侧淡出)
  • present 动画切换(下方淡入,下方淡出的模态切换)
  • 支持 HashRouter,BrowserRouter 两种路由
no_animation.gif
push_animation.gif

总体上,基本能达到 native 路由的切换体验,当然,毕竟是 web 页面,相对于 iOS 的原生的 native 路由体验还是有点差距,感兴趣的也可以 运行完整 demo体验下。。。

下面我们来简要看一下实现原理吧

简单来说,react-router-virgo是在 react-router 和 react-transition-group 的基础上进行二次封装的 Router。这里也会分这两部分进行递进分析

一、react-router

首先,我们先简要介绍下 react-router 的基本用法(详细看官网介绍)。

我们主要使用到 react-router 提供的 HashRouter/BrowserRouter,Switch,Route 三个组件。

1.1 Router

react-router 主要提供了两种 Router

  • HashRouter: hash 形式实现的路由,使用 createHashHistory 创建的 history

  • BrowserRouter:以 html5 提供的 history api 形式实现的路由,使用 createBrowserHistory 创建的 history

1.2 Route

路由组件,path 指定匹配的路由,component 指定路由匹配时展示的组件(Route 也可以通过 children 和 render 的形式创建展示的组件)。


1.3 Switch

多个 Route 组件同时匹配时,默认都会显示,但是被 Switch 包裹起来的 Route 组件只会显示第一个被匹配上的路由。路由的转场动画,其实就是基于 Switch 进行封装,使页面切换时,具有动画效果。

1.4 代码演示

使用react-router-dom中的 HashRouter/BrowserRouterRouteSwitch 实现的一个简单的无动画路由,代码如下:

import React from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom';
import { RouterConfig } from './routerConfig';

const Router = () => (
  // 这里使用 HashRouter , 也可以使用 BrowserRouter
  
    
      {RouterConfig.map((config, index) => {
        return ;
      })}
    
  
);

export default Router;

那怎么让页面切换的时候,带有转场动画效果呢,这就需要结合 react-transition-group 了

二、react-transition-group

在介绍实现转场动画之前,我们得先学习如何使用 react-transition-group。这里仅对我们使用到的 CSSTransitionTransitionGroup 这两个组件展开简要介绍。

2.1 CSSTransition

CSSTransition 是会自动给包裹的组件添加样式的过渡动画组件。我们先来看下 CSSTransition 的属性:

属性 详情
in 取值 true/false,决定包裹的元素是要进行出场动画还是入场动画
timeout 设置动画时间

实际上,CSSTransition 并没有给组件任何动画效果,只是在一段时间内,给包裹的组件加上三个类,我们可以在这个三类中写动画效果。

  • in 属性的值从 false 变为 true,就给元素加上下面的三个类(这里前缀xxx 代指设置的 class,默认是fade),即执行入场动画:
css 类 详情
xxx-enter 入场动画的第一个瞬间(帧)加入的,动画即将结束前消失
xxx-enter-active xxx-enter 加入后,第二个瞬间加入,持续到入场动画即将执行完成,动画即将结束前消失
xxx-enter-done 入场动画结束瞬间,加入之后一直存在
  • 相反地,当 in 属性置为 false 时,CSSTransition 会给子组件加上 xxx-exitxxx-exit-activexxx-exit-done 的 class。(更多详细介绍可以戳官网查看)

基于以上两点,我们给打开页面时的 class 设置为forward-from-right,则对应的 css 样式:

/**
 * 打开页面:右侧淡入,右侧淡出
 */
.forward-from-right-enter {
  z-index: 2;
  transform: translate3d(100%, 0, 0);
}

.forward-from-right-enter-active {
  z-index: 2;
  transform: translate3d(0, 0, 0);
  transition: all 300ms;
}

.forward-from-right-enter-done {
  z-index: 2;
}

.forward-from-right-exit {
  z-index: 1;
  transform: translate3d(0, 0, 0);
}

.forward-from-right-exit-active {
  z-index: 1;
  transform: translate3d(-30%, 0, 0);
  transition: all 300ms;
}

.forward-from-right-exit-done {
  z-index: 1;
}

2.2 TransitionGroup

用 CSSTransition 可以实现单个页面的动画,而路由的转场动画需要管理新旧两个页面的联动切换。那么如何让新旧页面在切换的过程中,同时存在两个 DOM 节点,在切换结束后,再移除旧节点呢?

为此我们再来介绍 react-transition-group 提供的TransitionGroup这个组件。

如官网介绍,TransitionGroup 组件就是用来管理一堆节点 mounting 和 unmounting 过程的组件,非常适合处理路由切换的情况。TransitionGroup 在感知到其 children 变化时,会先保存住即将要被移除的节点,而在其动画结束时才会真正移除该节点。

2.2 代码演示

使用 react-transition-group中的TransitionGroupCSSTransition 对 Switch 进行封装,实现路由转场动画组件AnimatedSwitch

import React from 'react';
import { Switch, withRouter } from 'react-router-dom';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import './AnimatedSwitch.css'; // 动画样式

let oldLocation = null;
class AnimatedSwitch extends React.Component {
  static propsTypes = {
    routerConfig: PropsTypes.array.isRequired,
  };
  render() {
    const { location, history, children, routerConfig } = this.props;
    let classNames = '';
    if (history.action === 'PUSH') {
      // 打开页面的转场动画
      classNames = 'forward-from-right';
    } else if (history.action === 'POP' && oldLocation) {
      // 关闭页面的转场动画
      classNames = 'back-to-right';
    }
    oldLocation = location;
    // 使用 TransitionGroup 和 CSSTransition 包裹 Switch,实现转场动画
    return (
       React.cloneElement(child, { classNames })}
      >
        
          {children}
        
      
    );
  }
}
// 通过 withRouter 包裹,可以从props中获取location,history等对象。
export default withRouter(AnimatedSwitch);

至此,路由转场动画算是基本实现了。

react-router-virgo 使用手册

一行代码 + 一个路由配置文件,就可以实现 react-router 的功能,并让你的Web页面切换达到匹配 Native 路由的转场动画体验

一、概述

react-router-virgo是在 react-router 和 react-transition-group 的基础上进行二次封装的 Router,使集成路由功能变得极其简单。此外,还增加了路由转场动画等扩展功能:无转场动画, Push 转场动画, Present 转场动画

二、安装 Router

### 使用npm
npm install --save react-router-virgo

### 使用yarn
yarn add react-router-virgo

三、 设置路由配置文件

设置路由配置项 RouterConfig.js

3.1 代码演示

import { Home, Detail, PushDetail, PresentDetail } from './pages/index';

// 例举了无动画、push转场动画、present转场动画三种场景
export const RouterConfig = [
  { path: '/', component: Home },
  // 无转场动画(新页面直接覆盖当前页面)
  {
    path: '/detail/:type',
    component: Detail,
    sceneConfig: {
      enter: 'no-animation',
      exit: 'no-animation',
    },
  },
  // push 转场动画(打开时,从左往右覆盖;关闭时,从右往左收回)
  {
    path: '/push/detail/:type/:id',
    component: PushDetail,
    sceneConfig: {
      enter: 'from-right',
      exit: 'to-right',
    },
  },
  // present 转场动画(打开时,从下往上弹起;关闭时,从上往下收起)
  {
    path: '/present/detail',
    component: PresentDetail,
    sceneConfig: {
      enter: 'from-bottom',
      exit: 'to-bottom',
    },
  },
];

3.2 配置项说明

key 说明 类型 默认值
path 路由路径,可以带参数,在/:后的为参数,如 /detail/:id, 参数为 id string 必传
component 路由路径映射的页面组件 class 必传
sceneConfig 路由转场动画配置,支持无动画、push 动画、present 动画三种场景,默认使用 push 动画 object {enter: 'from-right', exit: 'to-right'} 可选
exact 是否使用精准匹配 bool true 可选
  • 路由转场动画参数sceneConfig配置,支持以下三种场景
1.无动画配置:{
  enter: 'no-animation',
  exit: 'no-animation',
}
2.push动画配置: {
  enter: 'from-right',
  exit: 'to-right',
}
3.present动画配置: {
  enter: 'from-bottom',
  exit: 'to-bottom',
}

四、添加 Router

在入口文件App.js中添加Router

4.1 代码演示

import React from 'react';
import Router from './router/Router';
import { RouterConfig } from './RouterConfig';
import './index.css';

function App() {
  // RouterConfig 为路由配置文件
  return ;
}

export default App;

4.2 API 说明

属性 说明 类型 默认值
routerConfig 路由配置数据 array [] 必传
useBrowserRouter 路由类型 BrowserRouter/HashRouter,默认使用 HashRouter bool false 可选
useAnimatedSwitch 是否使用转场过渡动画 bool true 可选

如果

五. 常见问题

Q:支持哪些路由类型?

目前支持 BrowserRouter 和 HashRouter 两种类型,可通过属性useBrowserRouter来设置,默认使用 HashRouter

Q:支持哪些转场动画?

目前支持无动画、从下往上弹起的 Present 动画,从右往左打开的 Push 动画三种场景。可以在路由配置文件中按规则配置sceneConfig即可,如果未配置 sceneConfig 字段,则默认使用 Push 动画

Q:打开新页面后,上一级页面是否会被销毁?

会被销毁,返回上一级页面时,页面会重新渲染,后续版本迭代会支持 Stack 路由功能。

Q:安装后,编译失败的原因?

确认下项目里是否有 react-router-domreact-transition-group 这 2 个依赖,如果没有,请通过 yarn 或者 npm 引入依赖

yarn add react-router-dom react-transition-group

Q:是否支持 ts 开发的项目

后续迭代会支持

六. 其它

更具体的信息大家感兴趣的话去看代码吧,如果发现 bug,请提一个issue,我会第一时间进行修复和优化...

github: https://github.com/JackXJR/react-router-virgo

欢迎使用,觉得不错请给一个小小的 star 鼓励一下~

你可能感兴趣的:(基于react-router实现的一个媲美原生路由动画效果的Router)