React Animation 动画

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 文件中定义多个相应的样式。
比如说指定的 transitionNameexample,则需要定义命令如下的几个样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.example-enter { /* enter begin **/
}
.example-enter.example-enter-active { /* enter finish **/
}
.example-leave { /* leave begin **/
}
.example-leave.example-leave-active { /* leave finish **/
}
.example-appear{ /* appear begin **/
}
.example-appear.example-appear-active { /* appear begin **/
}

也就是需要定义 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-enterexample-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 { /* begin **/
opacity: 0.01;
margin-left: 128px;
}
.example-enter.example-enter-active { /* finish **/
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; /* TODO: make this a move animation */
transition: all 1s; /* TODO: make this a move animation */
width: 128px;
}

动画时间在 css 样式表中和 render 方法中,都必须指定,时间用来告诉 React 什么时候去移除 animation 样式以及什么时候从 DOM 中移除元素。

注意: 必须给 ReactCSSTransitionGroup 中的每一个 child 设置一个 key 属性,即使只渲染一个 element
组件第一次加载的时候,执行的动画是 appear,之后再有 element 动态添加到ReactCSSTransitionGroup,将会执行 enter 动画,而不会是 appear 动画;
transitionEntertransitionLeave 默认值是true,所以如果不把它们指定为false 的话得指定 transitionEnterTimeouttransitionLeaveTimeout

自定义 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