react-transition-group
库可以帮助开发者方便地实现组件进入和退出的过渡动画。
React 曾为开发者提供过动画插件
react-addons-css-transition-group
,后来改由社区维护,形成了现在的react-transition-group
库。
安装:
npm install react-transition-group --save
。
主要组件:
react-transition-group
库主要包含四个组件:
Transition:
该组件是一个和平台无关的组件。
CSSTransition:
CSSTransition 是基于 Transition 组件构建的,通过在合适的时机添加、移除类名来实现过渡效果。
在前端开发中,一般是结合 CSS 来完成样式,所以通常使用 CSSTransition 来完成过渡动画效果。
属性:
- in:触发组件的 enter 或者 exit 状态。
- 当 in 为 true 时,触发进入状态,会为组件添加
-enter
、-enter-active
的 class,开始执行动画;当动画执行结束后,会移除-enter
、-enter-active
的 class,并且添加-enter-dene
的 class。 - 当 in 为 false 时,触发退出状态,会为组件添加
-exit
、-exit-active
的 class,开始执行动画;当动画执行结束后,会移除-exit
、-exit-active
的 class,并且添加-exit-dene
的 class。
- 当 in 为 true 时,触发进入状态,会为组件添加
- unmountOnExit:设置当组件处于 exited 状态时卸载组件。
- timeout:设置过渡动画的时间。
- classNames:要添加的动画的类名前缀。
CSSTransition 执行过程中有三种状态:appear(初始状态)、enter(进入)、exit(退出)。这三种状态,需要定义对应的 CSS 样式:- 初始状态:对应的类名分别是
-appear
、-enter
、-exit
。 - 开始执行动画,对应的类名分别是
-appear-active
、-enter-active
、-exit-active
。 - 结束执行动画,对应的类名分别是
-appear-done
、-enter-done
、-exit-done
。
- 初始状态:对应的类名分别是
- appear:是否为初始状态添加动画。
钩子函数:
- onEnter:在进入动画执行之前被触发。
- onEntering:在进入动画开始执行时被触发。
- onEntered:在进入动画结束执行后被触发。
- onExit:在退出动画执行之前被触发。
- onExiting:在退出动画开始执行时被触发。
- onExited:在退出动画结束执行后被触发。
实现一个组件淡出淡入的动画效果。
// App.jsx
import {CSSTransition} from 'react-transition-group'
import './App.css'
class App extends PureComponent {
state = {
isShow: true,
}
render() {
const {isShow} = this.state
return (
<>
<button onClick={() => this.setState({isShow: !isShow})}>切换</button>
{/* 使用 CSSTransition 包裹要设置过渡动画的 React 元素,会在合适的时机为 React 元素添加、移除类名来实现过渡效果 */}
<CSSTransition in={isShow} unmountOnExit={true} timeout={2000} classNames='fade' appear onEnter={() => console.log('进入动画执行之前')}>
<h1>过渡动画</h1>
</CSSTransition>
</>
)
}
}
// App.css
/* 初始动画,进入动画 */
.fade-appear, .fade-enter {
opacity: 0;
}
.fade-appear-active, .fade-enter-active {
opacity: 1;
transition: opacity 2s ease;
}
/* 退出动画 */
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 2s ease;
}
SwitchTransition:
实现两个组件切换的动画效果。
SwitchTransition 需要包裹 Transition 或者 CSSTransition,单个的组件的动画效果还是需要由 Transition 或者 CSSTransition 实现,两个组件切换的动画效果由 SwitchTransition 实现。
属性:
- mode:过渡的模式。
out-in
:表示旧组件先移除,新组件再进入。in-out
:表示新组件先进入,旧组件再移除。
实现左侧退出一个组件,右侧进入一个组件的动画效果。
// App.jsx
import {SwitchTransition, CSSTransition} from 'react-transition-group'
import './App.css'
class App extends PureComponent {
state = {
isLogin: true,
}
render() {
const {isLogin} = this.state
return (
<>
{/* 使用 SwitchTransition 包裹 CSSTransition。mode 属性为 out-in,表示两个组件切换的模式是一个先出另一个再进 */}
<SwitchTransition mode='out-in'>
{/* 使用 CSSTransition 包裹要设置动画的 React 元素。需要设置不同的 key 属性 */}
<CSSTransition key={isLogin ? 'login' : 'logout'} timeout={2000} classNames='translate'>
<button onClick={() => this.setState({isLogin: !isLogin})}>
{isLogin ? '登录' : '退出'}
</button>
</CSSTransition>
</SwitchTransition>
</>
)
}
}
// App.css
/* 进入动画 */
.translate-enter {
transform: translateX(100px);
opacity: 0;
}
.translate-enter-active {
transform: translateX(0);
opacity: 1;
transition: all 2s ease;
}
/* 退出动画 */
.translate-exit {
transform: translateX(0);
opacity: 1;
}
.translate-exit-active {
transform: translateX(-100px);
opacity: 0;
transition: all 2s ease;
}
TransitionGroup:
当有一组动画时,需要将这些 Transition 或者 CSSTransition 放入到一个 TransitionGroup 中来完成动画。
一般用于列表中元素的动画。
属性:
- component:TransitionGroup 最终被渲染成的元素。默认情况下会被渲染成 一个 div 元素,也可以传入 null 将不会被渲染成任何元素。
// App.jsx
import {TransitionGroup, CSSTransition} from 'react-transition-group'
import './App.css'
class App extends PureComponent {
state = {
books: [
{
id: 1,
name: '你不知道的 JS',
price: '99',
}
],
}
handleAdd = () => {
let books = [...this.state.books]
books.push({
id: new Date().getTime(),
name: 'JS 高级程序设计',
price: 88,
})
this.setState({books})
}
handleDelete = index => {
let books = [...this.state.books]
books.splice(index, 1)
this.setState({books})
}
render() {
const {books} = this.state
return (
<>
{/* 使用 TransitionGroup 包裹一组想要实现动画的 React 元素 */}
<TransitionGroup>
{books.map((item, index) => (
// 使用 CSSTransition 包裹真正想要实现动画的 React 元素。需要设置唯一的 key 属性
<CSSTransition key={item.id} timeout={2000} classNames='fade'>
<li>
{item.name}: {item.price}
<button onClick={() => this.handleDelete(index)}>删除</button>
</li>
</CSSTransition>
))}
</TransitionGroup>
<button onClick={this.handleAdd}>添加一本书</button>
</>
)
}
}
/* 进入动画 */
.fade-enter {
opacity: 0;
}
.fade-enter-active {
opacity: 1;
transition: opacity 2s ease;
}
/* 退出动画 */
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0;
transition: opacity 2s ease;
}