React 动画效果之 TransitionGroup 的使用

2,334 阅读4分钟

这里记录一篇 React 实现动画效果的学习笔记,文章首发在个人博客站点 www.mayihahaha.com,欢迎吐槽。

TransitionGroup 是 React 提供的帮助实现动画效果的组件库,这里使用一个简单的 Demo 实践来演示这个库的使用。

未加动画效果的项目地址: github.com/wewin11235/…, 以这个项目为基础加上动画效果,读者可以自行 clone 并按照 README 文档在本地运行。

目标

在这个 todoList 应用中希望在添加或者删除一个 todo 项的时候加上淡入淡出的效果,而不是生硬的在页面上加入或者移除某一项。

TransitionGroup 的使用

使用前,首先需要安装 react-addons-css-transition-group

npm i react-addons-css-transition-group -D

要实现目标,只需要修改 todoList.js 文件:

import React from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import TodoItem from './todoItem.js';
import { toggleTodo, removeTodo } from '../actions.js';
import { FilterTypes } from '../../constants.js';
import TransitionGroup from 'react-addons-css-transition-group';  // 引入 ReactCssTransitionGroup, TransitionGroup 这个名字可以任意明明,因为需要的这个组件是 export default 的方式引入的
import './todoItem.css';   // 添加对应的 css

const TodoList = ({todos, onToggleTodo, onRemoveTodo}) => {
  return (
    <ul>
      <TransitionGroup transitionName="fade" transitionEnterTimeout={500}
        transitionLeaveTimeout={200}>
      {
        todos.map((item) => (
          <TodoItem
            key={item.id}
            text={item.text}
            completed={item.completed}
            onToggle={() => onToggleTodo(item.id)}
            onRemove={() => onRemoveTodo(item.id)}
          />
        ))
      }
      </TransitionGroup>
    </ul>
  );
};
.....

这里我们使用 TransitionGroup 包裹 TodoItem 组件数组,transitionName="fade" 代表这个TransitionGroup 相关的动画 CSS 的 class 都要以 fade 为前缀。

添加 CSS 文件 todoItem.css :

.fade-enter {
  opacity: 0.01;
}

.fade-enter.fade-enter-active {
  opacity: 1;
  transition: opacity 500ms ease-in;
}

.fade-leave {
  opacity: 1;
}

.fade-leave.fade-leave-active {
  opacity: 0.01;
  transition: opacity 200ms ease-in;
}

从 css 文件中可以看出,这里的 css class 都是 fade 为前缀(和 transitionName 的值一致),启动项目后,增加和删除代办事项会有动画效果,并且这里的 css class 是由 fade enterleave active 这些关键字按照规则组成的。

ReactCSSTransitionGroup 规则

通过上面的例子可以看出,ReactCSSTransitionGroup 需要依赖按照一定规则命名的 css class。在组件生命周期的不同阶段使用不同规则的 CSS 来达到动画效果。

类名规则

CSS 的类名需要以 transitionName 的值开头(如上例中的 fade),后面还有 enter、leave、active 这些关键字,用 - 连接。enter 代表 “装载” 开始时的状态,leave 表示 “卸载”开始时的状态,active 代表动画结束使用的状态。假设 transitionName 的值为 sample 那么相关的类名如下:

sample-enter
sample-enter-active
sample-leave
sample-leave-active

其中 sample-enter 和 sample-enter-active 是一对,实现了组件 “装载” 时候的动画效果,sample-leave 和 sample-leave-active 是一对,实现了组件 “卸载” 时候的动画。需要知道的是 enter 和 enter-active 并不是同时加到组件上的,因为 CSS3 的动画效果需要明确知道 CSS 的开始和结束时候的样式才能完成过度过程(不明白这个的需要学习下 CSS3 动画的相关知识点),所以对于载入而言,React 先让组件拥有 fade-enter 类,在 JavsScript 的下一个时钟周期才加上 fade-enter-active 类,这样就可以使用指定的动画过度方法,完成一个动画效果。对于“卸载” 过程也一样。

动画时间长度

TransitionGroup 动画的时长在两个地方都需要指定, 一个是 TransitionGroup 中以 Timeout 为结尾的属性(如上例中的 transitionEnterTimeout 和 transitionLeaveTimeout),第二个地方是 transition-duration 规则,即 CSS 里面。

一般来说这两处的值应该一致,分别来看看这两处值有何区别。

以上例中的 Todo 应用的 enter 过程为例,TransitionEnterTimeout 表示给组件加上 fade-enter 和 fade-enter-active 类 500 毫秒后就会把这两个类删除掉。CSS 中的规则表示在 500 毫秒内将动画按照指定的节奏运行完毕。所以当 TransitionEnterTimeout 小于 CSS 中指定的过度时间时,动画效果就会中途结束,并以普通效果展示出来。

装载时机

TransitionGroup 需要先加载完自身才能渲染内部组件。如上例中需要给一个数量变化的组件集体做动画的时候,TransitionGroup 需要包住这整个集合,这就是为了防止 TransitionGroup 自身没有渲染完带来的错误。

首次装载

如上例子中,todo 列表并不会在首次渲染到页面时候加载动画效果,只有在 todo 新加入(enter)和 删除(leavel)时候才会被调用,要想初次加载也有动画效果就需要 appear。

 <TransitionGroup transitionName="fade" transitionAppear={true} transitionAppearTimeout={500}>

不同于 enter 和 leavel, appear 需要有 transitionAppear={true} 的设置才能启用,这是因为 transitionEnter transitionLeavel 默认值为 true,transitionAppear 默认值为 false。appear 的 CSS 写法规则和 enter、leavel 一样。