all articles

simple transition with react-router

2017-03-09 @sunderls

react

最近用React开发welogger,想着在页面切换的时候如何搞个渐变。以下是一些笔记思考。

1. 首先把没有渐变的app搭起来

这里用create-react-app

> npm install -g create-react-app
> create-react-app transition
> cd transition
> npm start

可以看到app已经跑起来了。

2. 增加两个页面A, B

加入router

npm install --save-dev react-router

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router'
import App from './App';
import A from './A';
import B from './B';

ReactDOM.render(
    <Router history={browserHistory}>
        <Route path="/" component={App}>
            <IndexRoute component={A} />
            <Route path="A" component={A} />
            <Route path="B" component={B} />
        </Route>
    </Router>,
    document.getElementById('root')
);

app.js

import React from 'react';
import { Link } from 'react-router'

export default (props) => (
  <div>
    <Link to="/A">to A</Link><Link to="/B" style={{paddingLeft:'100px'}}>to B</Link>
    {props.children}
  </div>
  );

A/index.js

import React from 'react';
export default (props) => (
    <div>page A</div>
)

B/index.js

import React from 'react';
export default (props) => (
    <div>page B</div>
)

可以看到页面中出现了导航的两个链接,点击后分别会跳转到不同的页面,但是啪的一下就切换了,没有动画效果

3. 从右方滑入的效果

想了一下,可以用react自身的transition库。照着官网的例子: App.js

import './App.css';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
export default (props) => {
    return <div>
        <Link to="/A">to A</Link><Link to="/B" style={{paddingLeft:'100px'}}>to B</Link>
        <ReactCSSTransitionGroup
            transitionName="slide"
            transitionEnterTimeout={500}
            transitionLeaveTimeout={300}>
            {props.children}
        </ReactCSSTransitionGroup>
    </div>
}

App.css

.slide-enter {
    transform: translateX(100%);
}

.slide-enter.slide-enter-active {
    transform: translateX(0%);
    transition: transform 500ms ease-in;
}

.slide-leave {
    transform: translateX(0%);
}

.slide-leave.slide-leave-active {
    transform: translateX(-100%);
    transition: transform 300ms ease-in;
}

结果,没反应。原来单独transit一个child的话,需要给加上key,先随机加一个key

<ReactCSSTransitionGroup
    transitionName="slide"
    transitionEnterTimeout={500}
    transitionLeaveTimeout={300}>
    {React.cloneElement(props.children, {
        key: Math.random()
    })
</ReactCSSTransitionGroup>

这么一来是动起来了,不过在B页面点击到B的链接的时候,也发生了动画。估计是key每次都变化有关,改为pathname:

 <ReactCSSTransitionGroup
    transitionName="slide"
    transitionEnterTimeout={500}
    transitionLeaveTimeout={300}>
    {React.cloneElement(props.children, {
        key: props.location.pathname
    })}
</ReactCSSTransitionGroup>

非常顺利。然后进行优化一下,为了更顺畅,首先服父节点更换为<div>,并且再增加一个父节点,position:relative

<div className="container">
    <ReactCSSTransitionGroup
        component="div"
        transitionName="slide"
        transitionEnterTimeout={500}
        transitionLeaveTimeout={300}>
        {React.cloneElement(props.children, {
            key: props.location.pathname
        })}
    </ReactCSSTransitionGroup>
</div>

在transition进行的时候,需要保持位置持平,所以给animation中增加position的叙述。

.container {
  position: relative;
}

.slide-enter {
    position: absolute;
    width: 100%;
    transform: translateX(100%);
}

.slide-leave {
    position: absolute;
    width: 100%;
    transform: translateX(0%);
}

完美。

4. 点击A的时候想要反方向的话?

首先,transition是应该跟着页面走?不对,万一同一页面在不同的时候有不同的transition怎么办。

所以transition的变化,感觉应该在每次迁移的时候指定,也就是说感觉应该在链接上搞。 为此,新建一个<transLink>,点击的时候首先修改下一次transition,transition存在一个公共的object上。

transLink.js

import React from 'react';
import config from './config';

export default class TransLink extends React.PureComponent {
    onClick() {
        config.transition = this.props.transition || 'right';
    }

    render() {
        return <span onClick={this.onClick.bind(this)}>{this.props.children}</span>;
    }
}

config.js

export default {
    transition: 'right'
}

然后修改transitionName和链接。 app.js

import React from 'react';
import './App.css';
import { Link } from 'react-router'
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import config from './config';
import TransLink from './transLink'

export default (props) => {
    return <div>
        <TransLink transition="left">
            <Link to="/A">to A</Link>
        </TransLink>
        <TransLink transition="right">
            <Link to="/B" style={{paddingLeft:'100px'}}>to B</Link>
        </TransLink>
        <div className="container">
            <ReactCSSTransitionGroup
                component="div"
                transitionName={config.transition}
                transitionEnterTimeout={500}
                transitionLeaveTimeout={300}>
                {React.cloneElement(props.children, {
                    key: props.location.pathname
                })}
            </ReactCSSTransitionGroup>
        </div>
    </div>
}

以及css中,增加两种方向的slide

.right-enter {
    position: absolute;
    width: 100%;
    transform: translateX(100%);
}

.right-enter.right-enter-active ,
.left-enter.left-enter-active {
    transform: translateX(0%);
    transition: transform 500ms ease-in;
}

.right-leave, .left-leave {
    position: absolute;
    width: 100%;
    transform: translateX(0%);
}

.right-leave.right-leave-active {
    transform: translateX(-100%);
    transition: transform 300ms ease-in;
}

.left-enter {
    position: absolute;
    width: 100%;
    transform: translateX(-100%);
}

.left-leave.left-leave-active {
    transform: translateX(100%);
    transition: transform 300ms ease-in;
}

嗯,没有任何问题,OK。

5 总结

总结一下,要做切换首先要搞清楚transition的对象是谁,页面切换的时候,通常都是两个页面,所以transition需要放在页面切换部分之上的父元素。在那里以route的path做为key,然后用不同的css名来做animation。

关于animation还有一个很有人气的库react-motion,暂时还没有接触,后面再看看。