在本文中,我将向你展示如何使用 ReactTransitionGroup 和 Animated 库中的生命周期方法来实现页面的过渡效果。
你可以通过这个视频 http://animate.mhaagens.me 来观看演示效果。
让我们看看该怎样设置一些简单的路由动画!
1、安装React
首先安装 React 并创建一个 React 应用程序,很简单的就能创建一个 React 项目并让它运行。
如果你还没有安装 Create React App 就先装好(如果你已经安装,就跳过这一步):
npm install -g create-react-app
然后创建我们的项目:
create-react-app animatedroutes && cd animatedroutes
接下来安装 routes 和 animation 包:
yarn add react-router-dom animated react-transition-group
现在用你喜欢的编辑器打开项目,并运行它:
npm start
2、添加 React 路由
打开 src/index.js 文件,给 React 添加 BrowserRouter
import React from "react";import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App
import registerServiceWorker from "./registerServiceWorker";
import "./index.css";ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root"));registerServiceWorker();
然后添加两个需要渲染的组建,首先是 src/Home.js :
import React, { Component } from "react";
export default class Home extends Component {
render() {
return (
<div className="page">
Home
<p>Hello from the home page!</p>
</div>
)
}}
接着是 src/Subpage.js:
JavaScript
import React, { Component } from "react";
export default class Subpage extends Component {
render() {
return (
<div className="page">
Subpage
<p>Hello from a sub page!</p>
</div>
)
}}
下面打开src/App.js 文件并修改内容为:
JavaScript
import React, { Component } from 'react';
import { Route, Link } from "react-router-dom";
import Home from "./Home";
import Subpage from "./Subpage";
class App extends Component {
render() {
return (
<div className="App">
<div className="TopBar">
<Link to="/">Home</Link>
<Link to="/subpage">Subpage</Link>
</div>
<Route exact path="/" component={Home} />
<Route exact path="/subpage" component={Subpage} />
</div>
);
}}export default App;
最后删除 src/App.css 的内容,并把下面的代码复制到src/index.css 文件中:
CSS
html,
body,#root {
height: 100%;
width: 100%;}body {
margin: 0;
padding: 0;
font-family: sans-serif;}.App {
position: relative;
display: flex;
flex-flow: column;}.TopBar {
position: fixed;
top: 0;
left: 0;
display: flex;
flex-flow: row nowrap;
align-items: center;
width: 100%;
height: 62px;
padding: 0 24px;}.TopBar a {
margin-right: 18px;
text-decoration: none;}.animated-page-wrapper {
position: absolute;
top: 62px;
left: 0;
width: 100%;
height: 100%;}.page {
padding: 0 24px;}
好了,现在可以通过路由在主页面和子页面之间进行导航了。
3、添加 TransitionGroup
现在开始添加动画效果。我们需要做一些微不足道的工作来实现它。
现在,我们不再用默认的方式设置路由,而是要使用路由渲染方法来去渲染前面的组件,并将其封装到一个<TransitionGroup />中。
首先把TransitionGroup导入你的 src/App.js,像这样:
JavaScript
import TransitionGroup from "react-transition-group/TransitionGroup";
然后我们必须为 TransitionGroup 添加一个特殊的函数来渲染子组件。在 src/App.js文件中class App extends ...的前面添加这个函数:
JavaScript
const firstChild = props => {
const childrenArray = React.Children.toArray(props.children);
return childrenArray[0] || null;
};
然后删除你的路由,并替换成下面的代码:
Markup
<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>)}/>
您现在可以访问新的生命周期方法了,比如
componentWillAppear(),componentWillEnter()和componentWillLeave()。
让我们用它们来制作一个更高级的组件来实现我的的动画路由效果,现在好戏开场了!
4、创建Animated Wrapper 并用 Animated 实现动画
创建src/AnimatedWrapper.js文件并复制下面的代码到文件中:
JavaScript
import React, { Component } from "react";
import * as Animated from "animated/lib/targets/react-dom";
const AnimatedWrapper = WrappedComponent => class AnimatedWrapper
extends Component {
constructor(props) {
super(props);
this.state = {
animate: new Animated.Value(0)
};
}
render() {
return (
<Animated.div className="animated-page-wrapper">
<WrappedComponent {...this.props} />
</Animated.div>
);
}};
export default AnimatedWrapper; 这里有很多东西,我来解释一下。
我们用component来包装我们的路由组件。它将从 TransitionGroup 接收生命周期方法,我们可以用它来实现动画效果。 我们还用 Animated 创建了一个变量,可以用它来对封装的子组件中的 div 的不同样式属性实现动画效果。
让我们添加一些生命周期方法给组件添加动画效果。用Animated.template渲染,并且/或者插入动画状态值。
按照下面的代码修改src/AnimatedWrapper.js文件内容:
JavaScript
import React, { Component } from "react";
import * as Animated from "animated/lib/targets/react-dom";
const AnimatedWrapper = WrappedComponent => class AnimatedWrapper
extends Component {
constructor(props) {
super(props);
this.state = {
animate: new Animated.Value(0)
};
}
componentWillAppear(cb) {
Animated.spring(this.state.animate, { toValue: 1 }).start();
cb();
}
componentWillEnter(cb) {
setTimeout(
() => Animated.spring(this.state.animate, { toValue: 1 }).start(),
250
);
cb();
}
componentWillLeave(cb) {
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)
` };
return (
<Animated.div style={style} className="animated-page-wrapper">
<WrappedComponent {...this.props} />
</Animated.div>
);
}};export default AnimatedWrapper;
然后我们需要在每个路由组件中导入它,然后像这样将它们封装起来:
修改 src/Home.js 如下:
JavaScript
import React, { Component } from "react";
import AnimatedWrapper from "./AnimatedWrapper";
class HomeComponent extends Component {
render() {
return (
<div className="page">
Home
<p>Hello from the home page!</p>
</div>
)
}}const Home = AnimatedWrapper(HomeComponent);
export default Home;
修改 src/Subpage.js 如下:
JavaScript
import React, { Component } from "react";
import AnimatedWrapper from "./AnimatedWrapper";
class SubpageComponent extends Component {
render() {
return (
<div className="page">
Subpage
<p>Hello from a sub page!</p>
</div>
)
}}const Subpage = AnimatedWrapper(SubpageComponent);
export default Subpage;
就这样,现在你的页面切换效果应该是动态的了!
扩展阅读
我建议通过Animated文档来学习,但是现在相关文档很少。我们实用的Animated.template函数在 Github-issues 以外的地方几乎找不到。它的文档在这里:http://animatedjs.github.io/interactive-docs/。 你可以通过下面的链接下载Demo的演示视频: http://animate.mhaagens.me/ 或者: https://github.com/mhaagens/animated_routes_react
也可以关注我在Medium的博客或者我的Twitter,来学习更多 React 相关的内容。 https://twitter.com/mhaagens