在 React 中,组件切换、加载都可以添加上相应的动画,怎么去添加动画呢?该在什么地方添加呢?这些都不需要我们考虑,使用 react-addons-css-transition-group 可以快速的为组件加载和切换添加动画。
ReactCSSTransitionGroup
ReactCSSTransitionGroup 是官方提供的动画实现方案,它有同个重要的属性,用于配置动画。
- className: string - 动画根组件的根布局样式名。
- transitionName: string - 动画样式名,对应的动画样式需要在 css 文件中进行定义。
- transitionAppear: bool - 组件第一次加载的时候是否显示动画,默认值 false 没有动画。
- transitionEnterTimeout: int - 组件 enter 时动画超时时长,也就是动画最大时间。
- transitionLeaveTimeout: int - 同上,不同的是指定的是 leave 的时长。
- transitionAppearTimeout: int - 同上。
transitionName
重点说一下 transitionName ,该样式名指定的样式,需要在 css 文件中定义多个相应的样式。
比如说指定的 transitionName 为 example,则需要定义命令如下的几个样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| .example-enter { } .example-enter.example-enter-active { } .example-leave { } .example-leave.example-leave-active { } .example-appear{ } .example-appear.example-appear-active { }
|
也就是需要定义 enter / leave 初始、结束时的样式。
Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import React from 'react' import ReactDOM from 'react-dom' import ReactCSSTransitionGroup from 'react-addons-css-transition-group' const INTERVAL = 2000; var AnimateDemo = React.createClass({ getInitialState: function() { return {current: 0}; }, componentDidMount: function() { this.interval = setInterval(this.tick, INTERVAL); }, componentWillUnmount: function() { clearInterval(this.interval); }, tick: function() { this.setState({current: this.state.current + 1}); }, render: function() { var children = []; var pos = 0; var colors = ['red', 'gray', 'blue']; for (var i = this.state.current; i < this.state.current + colors.length; i++) { var style = { left: pos * 128, background: colors[i % colors.length] }; pos++; children.push(<div key={i} className="animateItem" style={style}>{i}</div>); } return ( <ReactCSSTransitionGroup className="animateExample" transitionEnterTimeout={1500} transitionLeaveTimeout={1500} transitionName="example"> {children} </ReactCSSTransitionGroup> ); } }); ReactDOM.render(<AnimateDemo/>, document.getElementById('root'));
|
在这个组件中,当添加一个元素到 ReactCSSTransitionGroup 中时,这个元素将会自动添加上 example-enter 和 example-enter-active 样式。
定义样式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| .example-enter, .example-leave { -webkit-transition: all 2s; transition: all 2s; } .example-enter { opacity: 0.01; margin-left: 128px; } .example-enter.example-enter-active { opacity: 1; margin-left: 0; } .example-leave { opacity: 1; margin-left: 0; } .example-leave.example-leave-active { opacity: 0.01; margin-left: -128px; } .animateExample { display: block; height: 128px; position: relative; width: 384px; } .animateItem { color: white; font-size: 36px; font-weight: bold; height: 128px; line-height: 128px; position: absolute; text-align: center; -webkit-transition: all 1s; transition: all 1s; width: 128px; }
|
动画时间在 css 样式表中和 render 方法中,都必须指定,时间用来告诉 React 什么时候去移除 animation 样式以及什么时候从 DOM 中移除元素。
注意: 必须给 ReactCSSTransitionGroup 中的每一个 child 设置一个 key 属性,即使只渲染一个 element!
组件第一次加载的时候,执行的动画是 appear,之后再有 element 动态添加到ReactCSSTransitionGroup,将会执行 enter 动画,而不会是 appear 动画;
transitionEnter 和 transitionLeave 默认值是true,所以如果不把它们指定为false 的话得指定 transitionEnterTimeout 和 transitionLeaveTimeout。
自定义 classes
除了通过 transitionName 指定样式,还可以指定每一步的动画样式,其中,active 样式名可以不用指定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| // ... <ReactCSSTransitionGroup transitionName={ { enter: 'enter', enterActive: 'enterActive', leave: 'leave', leaveActive: 'leaveActive', appear: 'appear', appearActive: 'appearActive' } }> {item} </ReactCSSTransitionGroup> <ReactCSSTransitionGroup transitionName={ { enter: 'enter', leave: 'leave', appear: 'appear' } }> {item2} </ReactCSSTransitionGroup> // ...
|
Animating One or Zero Items
上面所实现的动画,都是整个 Group 中的元素都将被设置动画,实际上还可以指定只需要某一/零个元素执行动画;
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; function ImageCarousel(props) { return ( <div> <ReactCSSTransitionGroup transitionName="carousel" transitionEnterTimeout={300} transitionLeaveTimeout={300}> <img src={props.imageSrc} key={props.imageSrc} /> </ReactCSSTransitionGroup> </div> ); }
|
Animation Group Must Be Mounted To Work
为了给 child 添加动画属性,ReactCSSTransitionGroup 必须已经被加载到 DOM 中,或者将 transitionAppear 设置成 true。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| render() { const items = this.state.items.map((item, i) => ( <div key={item} onClick={() => this.handleRemove(i)}> <ReactCSSTransitionGroup transitionName="example"> {item} </ReactCSSTransitionGroup> </div> )); return ( <div> <button onClick={this.handleAdd}>Add Item</button> {items} </div> ); }
|
上面这个例子将不会起作用,因为 ReactCSSTransitionGroup 被加载到新的 div 中,而不是已经加载好的元素当中,且没有设置 transitionAppear 为 false。
Disabling Animations
还可以禁止掉 enter 或者是 leave 动画,比如希望有 enter 动画而不想要 leave 动画,但是 ReactCSSTransitionGroup 需要等待动画完成后才从dom中 remove 元素,此时可以通过设置 transitionEnter={false} 或者 transitionLeave={false} 来禁止相应的动画;
More
在 ReactCSSTransitionGroup 中,没有动画完成的监听,所以如果想要获取到动画的执行进度并且添加其它操作是不可行的,如果确实有需要,可以使用 ReactTransitionGroup。