一、 react-transition-group简介
在开发中,我们想要给一个组件的显示和消失添加某种过渡动画,可以很好的增加用户体验。
当然,我们可以通过原生的CSS来实现这些过渡动画,但是React社区为我们提供了react-transition-group用来完成过渡动画。
React曾为开发者提供过动画插件 react-addons-css-transition-group,后由社区维护,形成了现在的 react-transition-group
这个库可以帮助我们方便的实现组件的 入场 和 离场 动画,使用时需要进行额外的安装
react-transition-group本身非常小,不会为我们应用程序增加过多的负担。
# npm
npm install react-transition-group --save
# yarn
yarn add react-transition-group
二、 主要组件
Transition
-
该组件是一个和平台无关的组件(不一定要结合CSS);
-
在前端开发中,我们一般是结合CSS来完成样式,所以比较常用的是CSSTransition;
CSSTransition
- 在前端开发中,通常使用CSSTransition来完成过渡动画效果
SwitchTransition
- 两个组件显示和隐藏切换时,使用该组件
TransitionGroup
- 将多个动画组件包裹在其中,一般用于列表中元素的动画;
三、 css-transition
CSSTransition是基于Transition组件构建的:
CSSTransition执行过程中,有三个状态:appear、enter、exit
它们有三种状态,需要定义对应的CSS样式: (-前用以填写自定义的样式前缀,也就是classNames)
- 第一类,开始状态:对于的类是-appear、-enter、exit;
- 第二类:执行动画:对应的类是-appear-active、-enter-active、-exit-active;
- 第三类:执行结束:对应的类是-appear-done、-enter-done、-exit-done;
简单案例
import React, { PureComponent } from 'react'
import { Card, Avatar, Button } from 'antd';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
import { CSSTransition } from 'react-transition-group'
import './style.css'
const { Meta } = Card
export default class App extends PureComponent {
constructor(props) {
super(props)
this.state = {
isShow: true
}
}
render() {
const { isShow } = this.state
return (
<>
<Button onClick={e => this.handleClick()}>显示/隐藏</Button>
{/*
将需要设置动画的组件放置到CSSTransition中
1. in 需要的是一个boolean值 用于控制动画的显示和隐藏
2. classNames 注意是classNames不是从className
用来设置transition的前缀 如果是card就会为 card-enter,card-exit
如果没有设置classNames就会默认为enter,exit,exit-done
3. timeout 用以设置动画的时长
注意: 动画的执行时长是由css中的transition的时间设置的
这里的timeout设置的是样式切换的时候,也就是-xxx-active经过多少秒后才会被转换为-xxx-done
如果这个时间设置的比transition中的动画时间短的话,因为样式会提前变为-xxx-done,所以整体
的动画执行时间会变为这个timeout所设置的时间,也就是说动画的执行时间会变短
推荐: 一般在设置的时候会将这2个时间进行统一,设置为一样的
*/}
<CSSTransition
in={isShow}
classNames="card"
timeout={300}
>
<Card
style={{ width: 300 }}
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title="Card title"
description="This is the description"
/>
</Card>
</CSSTransition>
</>)
}
handleClick() {
this.setState({
isShow: !this.state.isShow
})
}
}
style.css
.card-enter {
opacity: 0;
transform: scale(.6);
}
.card-enter-active {
opacity: 1;
transform: scale(1);
transition: opacity 300ms, transform 300ms;
}
/*
因为默认情况下 opacity就是1 scale的大小就是1
所以没有设置card-exit的必要性
*/
.card-exit-active {
opacity: 0;
transform: scale(.6);
transition: all 300ms;
}
/*
设置opacity为0的原因是 需要在退出后保持在不可见的状态
因为不可见,所以也就没有必要设置scale为0.6
*/
.card-exit-done {
opacity: 0;
}
属性配置
App.js
{/*
在CSSTransition上是有3个配置
1. enter 是否允许添加enter样式 default: true
2. exit 是否允许添加leave样式 defualt: true
3. apper 是否在第一次进入的时候就显示对应的动画 default: false
*/}
<CSSTransition
in={isShow}
classNames="card"
timeout={300}
appear
>
<Card
style={{ width: 300 }}
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title="Card title"
description="This is the description"
/>
</Card>
</CSSTransition>
style.css
/*
因为在这里enter的动画效果和apper所设置的动画效果是一致的
所以直接写在一起,但是如果不一致的话,需要重新设置
*/
.card-enter,
.card-appear {
opacity: 0;
transform: scale(.6);
}
.card-enter-active,
.card-appear-active {
opacity: 1;
transform: scale(1);
transition: opacity 300ms, transform 300ms;
}
.card-exit-active {
opacity: 0;
transform: scale(.6);
transition: all 300ms;
}
.card-exit-done {
opacity: 0;
}
钩子函数
{/*
有6个钩子函数: 每一个钩子函数中都一个参数el,表示的是CSSTransition中包裹的那个元素,这里指代的就是Card
onEnter
onEntering
onEntered
onExit
onExiting
onExited
unmountOnExit 用以在组件被隐藏的时候,卸载这个组件,在下次显示的时候再加载这个组件,default为false
*/}
<CSSTransition
in={isShow}
classNames="card"
timeout={300}
appear
unmountOnExit
onEnter={el => console.log('进入动画开始执行')}
onEntering={el => console.log('进入动画正在执行')}
onEntered={el => console.log('进入动画已经执行完毕')}
onExit={el => console.log('离场动画开始执行')}
onExiting={el => console.log('离场动画正在执行')}
onExited={el => console.log('离场动画已经执行完毕')}
>
<Card
style={{ width: 300 }}
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />}
title="Card title"
description="This is the description"
/>
</Card>
</CSSTransition>
四、SwitchTransition
CSSTransition是用来控制一个组件的显示和隐藏的,但是SwitchTransition是用来控制一个组件在进行切换的时候的进出场动画的,也就是说使用
SwitchTransition的时候,组件本质上一直是存在的,就是显示的内容在切换的时候添加的动画
TIPS:
- SwitchTransition组件里面要有CSSTransition或者Transition组件,不能直接包裹你想要切换的组件;
- SwitchTransition里面的CSSTransition或Transition组件不再像以前那样接受in属性来判断元素是何种状态,取而代之的是
key属性
App.js
import React, { PureComponent } from 'react'
import { SwitchTransition, CSSTransition } from 'react-transition-group'
import './style.css'
export default class App extends PureComponent {
constructor(props) {
super(props)
this.state = {
isActive: true
}
}
render() {
const {isActive} = this.state
return (
<div style={{textAlign: 'center'}}>
{/*
SwitchTransition 上有一个属性 mode 表示切换的方式
值为in-out和out-in 默认情况下是out-in,所以此处省略
*/}
<SwitchTransition>
{/*
在CSSTransition中需要将in属性切换为key属性
key的值只要不一样即可,以便于在合适的时候,可以根据key的改变来重新渲染CSSTransition
*/}
<CSSTransition
key={isActive ? 'on': 'off'}
classNames="btn"
timeout={500}
>
<button onClick={e => this.setState({isActive: !isActive})}>
{ isActive ? 'on' : 'off' }
</button>
</CSSTransition>
</SwitchTransition>
</div>
)
}
}
Style.css
.btn-enter {
opacity: 0;
transform: translateX(100%);
}
.btn-enter-active {
opacity: 1;
transform: translateX(0);
transition: all 1000ms;
}
.btn-exit-active {
opacity: 0;
transform: translateX(-100%);
transition: all 1000ms;
}
五、 TransitionGroup
App.js
import React, { PureComponent } from 'react'
import { CSSTransition, TransitionGroup } from 'react-transition-group'
import './style.css'
export default class extends PureComponent {
constructor(props) {
super(props)
this.state = {
persons: ['Klaus', 'John']
}
}
render() {
return (
<ul>
{/*
这个组件需要包裹住所有需要添加动画的元素
这个组件在实际的dom中会被渲染为一个div标签
*/}
<TransitionGroup>
{
this.state.persons.map( (item, index) => (
// CSSTransiton组件只能包裹一个元素
// 只要这个元素需要加上动画,那么这个元素一定需要使用CSSTransiton来进行包裹
// 因为in属性是用来控制这个组件在什么时候显示和隐藏,
// 这里只需要添加入场动画,所以可以不用设置in属性
<CSSTransition
classNames="person"
timeout={700}
key={index}
>
<li >{item}</li>
</CSSTransition>
))
}
</TransitionGroup>
<button onClick={ e => this.handleClick() }>Add</button>
</ul>
)
}
handleClick() {
this.setState({
persons: [...this.state.persons, 'Steven']
})
}
}
style.css
.person-enter {
opacity: 0;
transform: scale(.6);
}
.person-enter-active {
opacity: 1;
transform: scale(1);
transition: all 700ms;
}