React路由过渡动画

React 路由过渡动画

Animated page transitions with React Router 4, ReactTransitionGroup and
Animated

中文翻译: 英文原文: Animated.template文档 演示 Demo

原文中使用 create-react-app 从头构建路由动画,本文档只在如何在实际项目中使用路
由过渡动画。

安装 react-transition-group

需要注意的是开源作者修改了 V2 版本的 API,目前只能使用 V1 版本。

1
$ yarn add react-transition-group@^1.2.0

<TransitionGroup />

接下来只需简单的几个步骤

  • 替换路由默认的渲染方式,使用 <TransitionGroup /> 包裹路由组件并使用其中的路
    由渲染方法来渲染我们的组件。

    在路由入口文件 `src/App.js` 或是 `App.js` 中引入 `TransitionGroup`
    
    
    1
    import TransitionGroup from 'react-transition-  group/TransitionGroup';
  • Transition Group 渲染 添加一个特殊的方法,其只渲染单个 child,在 class App extends ... 之前添加。

    1
    2
    3
    4
        	const firstChild = props => {
    const childrenArray = React.Children.toArray(props.children);

    return childrenArray[0] || null; };
  • 修改原有的路由,并用 TransitionGroup 包裹

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        	<Route

    exact path="/" children={({ match, ...rest }) => (
    <TransitionGroup component={firstChild}> {match && <Home {...rest} />}
    </TransitionGroup> )}/>

    <Route

    path="/subpage" children={({ match, ...rest }) => (
    <TransitionGroup component={firstChild}> {match && <Subpage {...rest} />}
    </TransitionGroup> )}/>

    附:实际项目中的配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    const Root = () => {
    return (
    <Provider store={store}>
    <Router>
    <Switch>
    {routeList.mainRoute.map(item => {
    return (
    <Route
    path={item.path}
    key={item.path}
    children={({ match, ...rest }) => (
    <TransitionGroup component={firstChild}>
    {match && (
    <item.component
    {...rest}
    match={match}
    />
    )}
    </TransitionGroup>
    )}
    />
    );
    })}

    {<Redirect from="/" to={routePath.App} />}
    </Switch>
    </Router>
    </Provider>
    );
    };

新的生命周期方法

完成上面的工作,现在你有全访问新的生命周期方法了。

1
componentWillAppear() , componentWillEnter() and componentWillLeave()

创建高阶Animated组件

创建 AnimatedWrapper.js 组件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import React, { Component } from 'react';
import * as Animated from 'animated/lib/targets/react-dom';
import TransitionGroup from 'react-transition-group/TransitionGroup';

const AnimatedWrapper = WrappedComponent =>
class AnimatedWrapper extends Component {
constructor(props) {
super(props);
this.state = {
animate: new Animated.Value(0)
};
}
componentWillAppear(cb) {
// console.log('componentWillAppear');
Animated.spring(this.state.animate, { toValue: 1 }).start();
cb();
}
componentWillEnter(cb) {
// console.log('componentWillEnter');
setTimeout(
() =>
Animated.spring(this.state.animate, { toValue: 1 }).start(),
250
);
cb();
}
componentWillLeave(cb) {
// console.log('componentWillLeave');
Animated.spring(this.state.animate, { toValue: 0 }).start();
setTimeout(() => cb(), 175);
}
render() {
const style = {
opacity: Animated.template`${this.state.animate}`,
transform: Animated.template`
translate3d(0,${this.state.animate.interpolate({
inputRange: [0, 1],
outputRange: ['12px', '0px']
})},0)
`,
height: Animated.template`100%`
};
return (
<Animated.div style={style} className="animated-page-wrapper">
<WrappedComponent {...this.props} />
</Animated.div>
);
}
};

export default AnimatedWrapper;

上述代码主要做了这几件事

  • 创建一个组件包裹我们的路由组件
  • TransitionGroup 接收声明周期方法,用以完成animation
  • 使用 Animated 创建一个变量,可以用它来对封装的子组件中的 div 的不同样式属性
    实现动画效果

在路由组件中的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import React, { Component } from 'react';
import AnimatedWrapper from './AnimatedWrapper';
class HomeComponent extends Component {
render() {
return (
<div className="page">
<h1>Home</h1>
<p>Hello from the home page!</p>
</div>
);
}
}
const Home = AnimatedWrapper(HomeComponent);
export default Home;

当路由切换的的时候,我们就能看到过渡效果了!