安装:
这个库是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