Skip to content

深入动画

原生动画

在React中我们可以通过原生的CSS来实现过渡动画,但是React社区为我们提供了react-transition-group帮助我们快速过渡动画

使用原生的动画实现的动画过渡的效果

jsx

import React from "react";
import styled from 'styled-components'
const StyleDiv = styled.div`
width: ${props=>props.width};
height: ${props=>props.height};
background-color: skyblue;
opacity: ${props=>props.opacity};
transition: all 3s;
`

class App extends React.PureComponent{
    constructor(props) {
        super(props);
        this.state = {
            width:0,
            height:0,
            opacity:0
        }
    }

    render() {
        return(
           <div>
               <StyleDiv {...this.state}/>
               <button onClick={()=>{this.btnClick()}}>按钮</button>
           </div>
        )
    }
    btnClick(){
        this.setState({
            width:'100px',
            height:'100px',
            opacity:1
        })
    }
}

export default App

动画容器组件

在React中提供了四个容器组件来实现动画效果

Transition容器组件

  • 改组件是一个和平台无关的组件(不一定要结合CSS)
  • 在前端开发中,我们一般是结合CSS来完成过渡动画,所以比较常用的是CSSTransition

CSSTransition容器组件

  • 在前端开发中,通常使用CSSTransition来完成过渡动画效果

通过CSSTransition实现过渡效果

安装

shell script

npm i -S react-transition-group

使用

App.js

jsx

import React from "react";
import "./App.css"

import { CSSTransition } from "react-transition-group"

/**
 * in :取值为一个布尔值,如果取值为false,触发退出动画,如果取值是true触发进入动画
 * classNames: 指定动画类名的前缀
 * timeout: 设置动画的超时时间
 * unmountOnExit: 如果取值为true,表示动画结束后,删除对应的元素
 * appear: 是否显示初始化的动画效果
 */
class App extends React.PureComponent{
    constructor(props) {
        super(props);
        this.state = {
            isShow:false
        }
    }

    render() {
        return(
           <div>
               <CSSTransition in={this.state.isShow} classNames={'box'} timeout={3000} unmountOnExit={true}>
                   <div>

                   </div>
               </CSSTransition>
               <button onClick={()=>{this.btnClick()}}>按钮</button>
           </div>
        )
    }

    btnClick(){
        this.setState({
            isShow:!this.state.isShow
        })
    }
}

export default App

App.css

css

.box-enter{
    /**
    进入动画执行之前绑定的类名
    */
    width: 0;
    height: 0;
    opacity: 0;
    background-color: skyblue;
}

.box-enter-active{
    /**
    进入动画执行过程中绑定的类名
     */
    width: 100px;
    height: 100px;
    opacity: 1;
    transition: all 3s;
}

.box-enter-done{
    /**
    进入动画执行完毕之后绑定的类名
     */
    width: 100px;
    height: 100px;
    opacity: 1;
    background-color: red;
}


.box-exit{
    /**
    退出动画执行之前绑定的类名
    */
    width: 100px;
    height: 100px;
    opacity: 1;
    background-color: red;
}

.box-exit-active{
    /**
   退出动画执行过程中绑定的类名
    */
    width: 0;
    height: 0;
    opacity: 0;
    transition: all 1s;
}

.box-exit-done{
    /**
    退出动画执行完毕之后绑定的类名
     */
    width: 0;
    height: 0;
    opacity: 0;
    background-color: skyblue;
}

如果在一开始设置当前的盒子是处于显示的状态,上面的代码无法触发初试化的动画效果,这时候需要加入一个属性以及CSS的初始化的动画效果

jsx

constructor(props) {
    this.state = {
        isShow:true
    }
}

<CSSTransition in={this.state.isShow} classNames={'box'} timeout={1000}
               unmountOnExit={true} appear>

App.css

css

.box-appear{
    /**
    初始化动画执行之前绑定的类名
    */
    width: 0px;
    height: 0px;
    opacity: 0;
    background-color: skyblue;

}

.box-appear-active{
    /**
   初始化动画执行过程中绑定的类名
    */
    width: 100px;
    height: 100px;
    opacity: 1;
    transition: all 1s
}

.box-appear-done{
    /**
   初始化动画执行完毕之后绑定的类名
    */
    width: 100px;
    height: 100px;
    opacity: 1;
    background-color: red;
}

在CSSTransition中还有回调函数具体参考官网(翻墙):

地址为:https://reactcommunity.org/react-transition-group/transition

SwitchTransition容器组件

实现组件的切换动画,借鉴于vue transition modes

  • 两个组件显示与隐藏切换是,使用该组件

  • SwitchTransition可以完成组件切换的动画

  • SwitchTransition组件里面要有CSSTransition或者Transition组件,不能直接包裹你想要切换的组件

  • SwitchTransition里面的CSSTransition或者Transition组件不再像以前一样接收in属性来判断元素是何种状态,需要通过key属性

示例

jsx

import React from "react";
import "./App.css"
import { CSSTransition,SwitchTransition } from "react-transition-group"
class App extends React.PureComponent{
    constructor(props) {
        super(props);
        this.state = {
            isOn:true
        }
    }

    render() {
        return(
           <div>
               <SwitchTransition>
                   <CSSTransition key={this.state.isOn} timeout={3000} classNames={'btn'}>
                       <button onClick={()=>{this.setState({isOn:!this.state.isOn})}}>{this.state.isOn ?'on':'off'}</button>
                   </CSSTransition>
               </SwitchTransition>
           </div>
        )
    }
}

export default App

App.css

css

.btn-enter{
    /**
    进入动画执行之前绑定的类名
    */
    opacity: 0;
    transform: translateX(-100%);
}

.btn-enter-active{
    /**
    进入动画执行过程中绑定的类名
     */
    opacity: 1;
    transform: translate(0);
    transition: all 3s;
}

.btn-enter-done{
    /**
    进入动画执行完毕之后绑定的类名
     */

}

.btn-exit{
    /**
    退出动画执行之前绑定的类名
    */
    opacity: 1;
    transform: translate(0);
}

.btn-exit-active{
    /**
   退出动画执行过程中绑定的类名
    */
    opacity: 0;
    transform: translate(100%);

    transition: all 3s;
}

.btn-exit-done{
    /**
    退出动画执行完毕之后绑定的类名
     */
}

button{
    padding: 10px 20px;
    margin-left: 50%;
}

TransitionGroup容器组件

  • 将多个动画组件包裹在其中,一般用于列表中元素的动画

当我们有一组组件需要执行动画时,就需要将这些CSSTransition放入到一个TransitionGroup中来完成动画

和SwitchTransition一样都需要使用key来触发

示例:

jsx

import React from "react";
import "./App.css"

import { CSSTransition,TransitionGroup } from "react-transition-group"

class App extends React.PureComponent{
    constructor(props) {
        super(props);
        this.state = {
            heroList:['虞姬','鲁班','火舞']
        }
    }

    render() {
        return(
            <div>
                {/*
                注意点:无论是TransitionGroup还是SwitchTransition都必须是它直接的子元素才可以
                */}
                    <ul>
                        <TransitionGroup>

                        {
                            this.state.heroList.map((v,index)=>{
                                return (
                                    <CSSTransition key={v} timeout={3000} classNames={'item'}>
                                        <li key={v+index} onClick={()=>{this.removeHero(index)}}>{v}</li>
                                    </CSSTransition>
                                )
                            })
                        }
                        </TransitionGroup>
                    </ul>
                <button onClick={()=>{this.btnClick()}}>新增</button>
            </div>
        )
    }
    btnClick(){
        this.setState({
            heroList:[...this.state.heroList,...['你好']]
        })
    }
    removeHero(index){
        const list = this.state.heroList.filter((name,idx)=>{
            return idx !==index
        })
        this.setState({
            heroList:list
        })
    }
}

export default App

App.css

css

.item-enter{
    /**
    进入动画执行之前绑定的类名
    */
    opacity: 0;
    transform: translateX(100%);
}

.item-enter-active{
    /**
    进入动画执行过程中绑定的类名
     */
    opacity: 1;
    transform: translate(0);
    transition: all 3s;
}

.item-enter-done{
    /**
    进入动画执行完毕之后绑定的类名
     */

}

.item-exit{
    /**
    退出动画执行之前绑定的类名
    */
    opacity: 1;
    transform: translate(0);
}

.item-exit-active{
    /**
   退出动画执行过程中绑定的类名
    */
    opacity: 0;
    transform: translate(100%);

    transition: all 3s;
}

.item-exit-done{
    /**
    退出动画执行完毕之后绑定的类名
     */
}

button{
    padding: 10px 20px;
    margin-left: 50%;
}

注意点

在企业开发中,一定要保证CSSTransition中key的唯一性,不然重复的key,元素不会被创建

Released under the MIT License.