寫在前面
之前我寫過關(guān)于react-transition-group和react-motion的使用教程灸蟆,就控制粒度上來說囤耳,react-motion要好很多,但是react-motion有個(gè)比較麻煩的問題,就是我暫時(shí)沒找到開箱即用的動(dòng)畫封裝亿蒸,而用react-motion也沒法使用animate.css,所以如果項(xiàng)目僅僅只用于web端掌桩,沒考慮native边锁,那么建議還是使用react-transition-group,會(huì)方便很多
用到的第三方庫
- react-router-dom 4.2.2 用于路由
- react-transition-group 2.2.1 用于react動(dòng)畫實(shí)現(xiàn),這里需要注意下波岛,使用的不是版本1茅坛,而是只包含{Transition, TransitionGroup, CSSTransition}的版本2
- animate.css 用于動(dòng)畫效果
熱身
在正式開始寫路由的切換動(dòng)畫前,我們先用react-transition-group
結(jié)合animate.css
來實(shí)現(xiàn)一個(gè)簡單的組件進(jìn)出場(chǎng)動(dòng)畫则拷,以此回顧之前關(guān)于react-transition-group
的知識(shí)
react-transition-group文檔
<div>
<button onClick={this.toggleState}>click</button>
{/*第一個(gè)例子*/}
<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>
效果
代碼很簡單贡蓖,用
in
控制組件的顯示和隱藏曹鸠,用classNames
控制組件進(jìn)出場(chǎng)的className,稍微需要注意的是與animate.css
的結(jié)合方式
路由切換
回顧了組件的進(jìn)場(chǎng)和出場(chǎng)動(dòng)畫實(shí)現(xiàn)后斥铺,我們正式來開始寫路由的切換動(dòng)畫彻桃。先理清楚思路,在react-router4
里面晾蜘,每個(gè)路由對(duì)應(yīng)其實(shí)就是一個(gè)組件邻眷,無非就是在路由匹配到的時(shí)候,將CSSTransition
的in
設(shè)置為true
剔交,不匹配設(shè)置為false
肆饶,僅此而已。
唯一麻煩的地方在于怎么獲取路由的匹配信息省容,翻看react-router4
的api,我們看到,Route
和渲染有關(guān)的props有三個(gè),component
,render
,children
,component
和render
都拿不到匹配信息抖拴,只要路由匹配到,組件就會(huì)mount,反之腥椒,就會(huì)unmount阿宅,我們無法進(jìn)行控制,而children
正好符合我們的期望笼蛛,它與render
類似洒放,不同的地方在于無論path是否匹配,都會(huì)被觸發(fā)滨砍,然后會(huì)將當(dāng)前的match信息傳遞過來往湿,我們也正好可以通過match來控制CSSTransition
先寫一個(gè)無動(dòng)畫的路由切換
不管怎么樣,我們先寫個(gè)簡單的路由切換惋戏,然后再對(duì)其進(jìn)行改裝
主路由
<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>
}
}
簡單的路由就寫好了领追,接下來考慮加動(dòng)畫
利用高階組件給頁面加上動(dòng)畫
我并不希望在頁面內(nèi)部實(shí)現(xiàn)動(dòng)畫邏輯,首先是頁面邏輯與動(dòng)畫邏輯無關(guān)响逢,其次如果每寫一個(gè)頁面就寫一次動(dòng)畫邏輯绒窑,我怕是要累死,所以我們這里將動(dòng)畫邏輯單獨(dú)抽取出來舔亭,封裝成一個(gè)高階組件
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>
接下來订雾,直接看效果吧
總結(jié)
和普通的組件切換動(dòng)畫差不多,只是需要注意下怎么在react-router4的路由中獲取組件的顯示狀態(tài)
完整的代碼我放到了github上: https://github.com/soyal/router4-transition
感謝你的閱讀