安裝:
這個庫是react官方自帶的,它實現於react/lib/ReactCSSTransitionGroup.js。
你可以通過import直接導入這個文件,或者通過命令來安裝一個便捷的別名包(僅僅是指向react/lib/ReactCSSTransitionGroup.js):
npm install –save react-addons-css-transition-group
ReactCSSTransitionGroup組件參數
ReactCSSTransitionGroup其實就是一個組件,它規定了特定的參數,我們通過設置這些特定的參數,將這些參數反應到被其包裹的子組件中。下面,我們就其幾個常見的參數進行講解。
# transitionName: 設置動態生成類的自定義前綴,如果我們設置為carousel-image-item,那么,就會相應的生成carousel-image-item-enter, carousel-image-item-enter-active等。
# component: 字符串,設置ReactCSSTransitionGroup生成包裹子組件的標簽,默認時span,我們可以通過這個參數自定義,如div。
# transitionEnter: 布爾值,設置是否使用出場動畫,默認時true。
# transitionEnterTimeout: 數值,設置入場動畫的執行時間,需要在css中和這里同時設置,否則會提示警告。
# transitionLeave: 布爾值,設置是否使用出場動畫,默認時true。
# transitionLeaveTimeout: 數值,設置出場動畫的執行時間,需要在css中和這里同時設置,否則會提示警告。
最后,說一下transitionAppear,它和其他兩個不同,它默認時false,默認不執行。
原理
ReactCSSTransitionGroup也是一個react組件,我們將在react-router的路由容器組件中引用它,讓它替我們在路由切換的時候實現頁面間的過渡動畫。
首先看一下我的路由配置:
ReactDOM.render( ( <Provider store={store}> <Router history={history}> <Route path="/" component={Container}> <IndexRoute component={MsgListPage} /> <Route path="msg-list-page" component={MsgListPage}/> <Route path="msg-detail-page/:msgId" component={MsgDetailPage}/> <Route path="msg-create-page" component={MsgCreatePage}/> <Route path="menu-page" component={MenuPage}/> </Route> </Router> </Provider> ), document.getElementById('reactRoot') );
一個很簡單的路由配置,所有子路由的父容器都是Container組件,路由切換時react-router會將代表子路由的組件(例如MsgListPage)填充到Container的props.children孩子屬性中。
既然Container組件是容納子路由組件的容器,那么可以想到當子路由切換時:Conainter的props.children經歷了從老的組件變為了新的組件的過程,如果可以在這個過程中稍作手腳是有機會實現新老組件的平滑過渡的。
先來看一下當前Container當前實現:
import React from "react"; export default class Container extends React.Component { constructor(props, context) { super(props, context); } componentWillMount() { document.body.style.margin = "0px"; // 這是防止頁面被拖拽 document.body.addEventListener('touchmove', (ev) => { ev.preventDefault(); }); } render() { return ( <div id="reactContainer"> { this.props.children } </div> ); } }
它將子路由組件(也就是this.props.children)直接填充了進來,這樣實現雖然能夠完成路由切換,但是它沒有任何的過渡效果。
下面利用ReactCSSTransitionGroup實現過渡效果,代碼變成了這樣:
import React from "react"; import ReactCSSTransitionGroup from "react-addons-css-transition-group"; import style from "./Container.css"; export default class Container extends React.Component { constructor(props, context) { super(props, context); } componentWillMount() { document.body.style.margin = "0px"; // 這是防止頁面被拖拽 document.body.addEventListener('touchmove', (ev) => { ev.preventDefault(); }); } render() { return ( <ReactCSSTransitionGroup transitionName="transitionWrapper" component="div" className={style.transitionWrapper} transitionEnterTimeout={300} transitionLeaveTimeout={300}> <div key={this.props.location.pathname} style={{position:"absolute", width: "100%"}}> { this.props.children } </div> </ReactCSSTransitionGroup> ); } }
我們直接套用了ReactCSSTransitionGroup組件,並將子路由組件(this.props.children)包裹在其內部,這樣做的目的是:當子路由組件切換時,ReactCSSTransitionGroup可以攔截其內部新老組件的交替過程,從而實現老組件消逝,新組件出現的過渡視覺。
說了那么多,不如看一下切換路由的瞬間DOM樹的樣子,更加便於理解:

外層div是ReactCSSTransitionGroup引入的父<div>,它內部是有2個子<div>是這段代碼引入的:
<div key={this.props.location.pathname}
style={{position:"absolute", width: "100%"}}>
默認同一時刻應該只有1個路由組件,那么<div>為什么會出現2個呢?因為ReactCSSTransitionGroup攔截了子路由切換的過程,它在組件替換前將前1個子組件備份了起來,在替換后將新老2個子組件一起填充到父<div>中並開始執行過渡動畫,當動畫結束后它將老組件移除只保留下新組件:

為什么子<div>要有一個key屬性呢?因為ReactCSSTransitionGroup在過渡期間同時維護新老組件需要一個唯一標識加以區分,因為location.pathname代表當前訪問的完整路徑(包括_k=…),所以用它最合適不過。
CSS動畫
於動畫是怎么實現的?第一張圖片里你應該可以看到,它為2個子<div>添加了對應的class,一個是enter進入的意思,另外一個是leave離開的意思,我們只需要定義對應的css實現transition動畫既可(注意<ReactCSSTransitionGroup>的transitionName屬性定義了下述class的前綴):
:global(.transitionWrapper-enter) { opacity: 0.01; transition: opacity 30000ms ease-in; } :global(.transitionWrapper-enter.transitionWrapper-enter-active) { opacity: 1; } :global(.transitionWrapper-leave) { opacity: 1; transition: opacity 30000ms ease-in; } :global(.transitionWrapper-leave.transitionWrapper-leave-active) { opacity: 0; } .transitionWrapper { position: relative; }
這里,:global(classname)的用法是css-loader插件提供的,默認所有css都是通過css-loader局部編譯的,從而保證跨組件css名字不沖突
基於ReactCSSTransitionGroup實現react-router過渡動畫:https://www.cnblogs.com/qq120848369/p/6066837.html
第二種方式:

React代碼為:
import React, { Component } from 'react'; import { NavLink } from 'react-router-dom'; import PropTypes from 'prop-types'; import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; import './header.css'; export default class PublicHeader extends Component{ static propTypes ={ record:PropTypes.any, title:PropTypes.string.isRequired, confirm:PropTypes.any, } state={ navState:false, //導航欄是否顯示 }; //切換左側導航欄狀態 toggleNav = () =>{ this.setState({navState:!this.state.navState}); } // css動畫組件設置為目標組件 FirstChild = props =>{ const childrenArray = React.Children.toArray(props.children); return childrenArray[0] || null; } render(){ return( <header className="header-container"> <span className="header-slide-icon icon-catalog" onClick={this.toggleNav}></span> <span className="header-title">{this.props.title}</span> <ReactCSSTransitionGroup component={this.FirstChild} transitionName="nav" transitionEnterTimeout={300} transitionLeaveTimeout={300}> { this.state.navState && <aside key='nav-slide' className="nav-slide-list" onClick={this.toggleNav}> <NavLink to="/" exact className="nav-link icon-jiantou-copy-copy">首頁</NavLink> <NavLink to="/brokerage" exact className="nav-link icon-jiantou-copy-copy">提現</NavLink> <NavLink to="/helpcenter" exact className="nav-link icon-jiantou-copy-copy">幫助中心</NavLink> </aside> } </ReactCSSTransitionGroup> </header> ) } }
CSS代碼為:
.nav-enter{ transform: translate3d(-100%, 0, 0); } .nav-enter.nav-enter-active{ transform: translate3d(0, 0, 0); transition: transform 300ms; } .nav-leave{ transform: translate3d(0, 0, 0); } .nav-leave.nav-leave-active{ transform: translate3d(-100%, 0, 0); transition: transform 300ms; }

離開的時候狀態

第二種方式,居中彈窗

React代碼為:
import React,{ Component } from 'react';
import PropTypes from 'prop-types';
import { is, fromJS } from 'immutable';
import TouchableOpacity from '@/components/TouchableOpacity/TouchableOpacity';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import './alert.css';
export default class Alert extends Component{
static propTypes = {
closeAlert: PropTypes.func.isRequired,
alertTip: PropTypes.string.isRequired,
alertStatus: PropTypes.bool.isRequired,
}
// css動畫組件設置為目標組件
FirstChild = props => {
const childrenArray = React.Children.toArray(props.children);
return childrenArray[0] || null;
}
// 關閉彈框
confirm = () => {
this.props.closeAlert();
}
shouldComponentUpdate(nextProps, nextState){
return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
}
render(){
return (
<ReactCSSTransitionGroup
component={this.FirstChild}
transitionName="alert"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}>
{
this.props.alertStatus&&<div className="alert-con">
<div className="alert-context">
<div className="alert-content-detail">{this.props.alertTip}</div>
<TouchableOpacity className="confirm-btn" clickCallBack={this.confirm}/>
</div>
</div>
}
</ReactCSSTransitionGroup>
)
}
}
CSS代碼為:
.alert-enter{ opacity: 0; } .alert-enter.alert-enter-active{ transition: all 300ms; opacity: 1; } .alert-leave{ opacity: 1; } .alert-leave.alert-leave-active{ transition: all 300ms; opacity: 0; }
進去時候

離開時候

第三種方式:居中彈窗

React中居中插件Component
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
export default class Alert extends Component {
static PropTypes ={
closeAlert: PropTypes.func.isRequired,
alertStatus: PropTypes.bool.isRequired
}
confirm =()=>{
this.props.closeAlert()
}
render() {
console.log(this.props)
return (
<ReactCSSTransitionGroup
component="div"
transitionName="fade"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}>
{
this.props.alertStatus&&<div className="flashfixed">
<div className="flash_cover_bg"></div>
<div className="flash_cover_content">
<div className="flash_cover_wrap">
<div className="flash_name">練習</div>
<ul className="flashUl"></ul>
<a className="sku-close" href="javascript:void(0)" onClick={this.confirm}><i className="icon-circle"></i></a>
</div>
</div>
</div>
}
</ReactCSSTransitionGroup>
);
}
}
css代碼為:
.flashfixed{ position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 99;opacity:1} .flashfixed.fade-enter{opacity:0;} .flashfixed.fade-enter.fade-enter-active{transition:opacity 0.3s 80ms ;opacity:1;pointer-events:none} .flashfixed.fade-leave{opacity:1;} .flashfixed.fade-leave.fade-leave-active{transition:opacity 0.3s 80ms ;opacity:0;} .flash_cover_content{ position: fixed; left: 50%; top: 0; bottom: 0; top: 50%; width: 476px; height: 476px; margin-left: -238px; margin-top: -238px; background:#ffe30d; border-radius:50px; -webkit-transform:scale(1);transform:scale(1);} .flashfixed.fade-enter .flash_cover_content{-webkit-transform:scale(0.5);transform:scale(0.5);} .flashfixed.fade-enter.fade-enter-active .flash_cover_content{-webkit-transform:scale(1);transform:scale(1); -webkit-transition:-webkit-transform .3s cubic-bezier(0,0,.25,1) 80ms;transition:transform .3s cubic-bezier(0,0,.25,1) 80ms;} .flashfixed.fade-leave .flash_cover_content{-webkit-transform:scale(1);transform:scale(1);} .flashfixed.fade-leave.fade-leave-active .flash_cover_content{-webkit-transform:scale(0.5);transform:scale(0.5); -webkit-transition:-webkit-transform .3s cubic-bezier(0,0,.25,1) 80ms;transition:transform .3s cubic-bezier(0,0,.25,1) 80ms;}
進去時候:

退出時候

本地效果為:D:\www\svn\project\react_abacus\src\components\alert\Alert.js
