React@16.x(33)动画(上)

目录

使用 react-transition-group 来实现动画。总共有4个动画组件,覆盖大多应用场景。

1,Transition

官方文档

过渡动画的原理,和 Vue的过渡动画 类似。分为2个阶段:

  1. 进入(enter)过渡动画,对应的状态有3个,
    1. 进入前的初始状态 default
    2. 动画进行中的状态 entering
    3. 进入后的结束状态 entered
  2. 退出(exit / leave)过渡动画,对应的状态也是3个,
    1. 退出前的初始状态 entered
    2. exiting
    3. exited

注意

进入前的初始状态和 exited 是一个状态。

退出前的初始状态和 entered 也是一个状态。

以一个渐入渐出动画举例,

css 复制代码
.default {
	transition: opacity 2s ease-in-out;
}
.default, .exiting, .exited{
	opacity: 0;
}
.entering, .entered {
	opacity: 1;
}

官方文档的例子:

js 复制代码
import { Transition } from "react-transition-group";
import { useRef, useState } from "react";

// 过渡时间
const duration = 300;

// 默认样式
const defaultStyle = {
    transition: `opacity ${duration}ms ease-in-out`,
    opacity: 0,
};

// 过渡样式
const transitionStyles = {
    entering: { opacity: 1 },
    entered: { opacity: 1 },
    exiting: { opacity: 0 },
    exited: { opacity: 0 },
};

/**
 * Transition.children 是一个函数,函数的返回值是要进行动画的元素。
 * 函数会随着过渡动画的进行而调用(进入动画会调用3次,对应3种状态),参数 state 就是当前过渡状态。
 * @param {in} boolean,动画开关。从 false-> true,开启进入动画,反之退出动画。
 * @returns
 */
function Fade({ in: inProp }) {
    const nodeRef = useRef(null);
    return (
        <Transition
            nodeRef={nodeRef}
            in={inProp}
            timeout={duration}
            addEndListener={() => {
                nodeRef.current.addEventListener(
                    "transitionend",
                    () => {
                        console.log("过渡结束");
                    },
                    { once: true }
                );
            }}
        >
            {(state) => (
                <div
                    ref={nodeRef}
                    style={{
                        ...defaultStyle,
                        ...transitionStyles[state],
                    }}
                >
                    I'm a fade Transition!
                </div>
            )}
        </Transition>
    );
}

export default function App() {
    const [visible, setVisible] = useState(true);
    return (
        <>
            <button
                onClick={() => {
                    setVisible(!visible);
                }}
            >
                切换动画
            </button>
            <Fade in={visible} />
        </>
    );
}

效果:

也可以不用内联样式,而用 class 来添加更多的样式:

html 复制代码
<div className={state}>I'm a fade Transition!</div>

一些常用 props

1,mountOnEnter

延迟挂载 。顾名思义,在进入动画开始前再加载要进行动画的元素。默认 false 立即加载。

比如,对渐入渐出动画来说,如果初始值 in={false},也就是说元素一开始是隐藏的。

  • 默认情况下 mountOnEnter={false} 元素依然会被挂载到 DOM 中,等待动画开始。
  • 如果设置 mountOnEntermountOnEnter={true} 则元素延迟挂载,一开始并不会挂载到DOM中,直到动画开始才挂载并进行动画。

2,unmountOnExit

顾名思义,在退出动画结果后,是否在DOM中直接卸载动画元素。默认 false 不卸载。

3,appear

默认情况下,在元素挂载阶段,如果 in={true} 则直接进入动画的最终状态,整个过程 Transition.children 只会调用一次,参数 state=entered

此时设置 appearappear={true},则进入动画会经历完整3个阶段,函数也会运行 3 次。

注意,Transition 组件中并没有提供 appear 这个状态,所以也无法设置改状态下的样式。所以只是函数会运行3次而已

如果想一开始就执行一次进入的过渡动画,得使用下面这个组件 CSSTransition

还有其他的一些属性不多介绍了,比如设置过渡动画结束后的回调函数等。用到时参考官方文档即可。

2,CSSTransition

官方文档

2.1,和 Transition 组件的区别

  1. CSSTransition 是在 Transition 的基础上实现的,可以理解为是它的增强版,同时继承了它的所有属性 props。
  2. Transition 只是提供了基础的进入和退出动画,并将过渡状态 state 暴露出来,通过它来设置样式。更复杂的动画需要用到 onEnter 等回调函数来实现。
    CSSTransition 是基于 CSS 的过渡动画组件,只需要为不同的过渡状态指定相应的类名(classNames),CSSTransition 会自动在适当的时机添加或删除这些类。
  3. CSSTransition* 能够实现加载动画

2.2,举例

js 复制代码
import { CSSTransition } from "react-transition-group";
import { useState } from "react";
import "./App.css";

export default function App() {
    const [inProp, setInProp] = useState(true);
    return (
        <div>
            <CSSTransition in={inProp} timeout={200} classNames="crane">
                <div>classNames 是自定义类名前缀</div>
            </CSSTransition>
            <button type="button" onClick={() => setInProp(!inProp)}>
                进入/退出
            </button>
        </div>
    );
}
css 复制代码
.crane-enter {
    opacity: 0;
}
.crane-enter-active {
    opacity: 1;
    transition: opacity 200ms;
}
.crane-enter-done {
    opacity: 1;
}
.crane-exit {
    opacity: 1;
}
.crane-exit-active {
    opacity: 0;
    transition: opacity 200ms;
}

.crane-exit-done {
    opacity: 0;
}

逻辑,动画效果和 Transition 组件差不多。同样的,进入和退出动画分别有3种状态,不过直接对应到类名上了:

  • 进入动画
    1. enter,动画开始前的初始化类名。
    2. enter-active,动画进行中的类名。
    3. enter-done,动画结束后的类名。
  • 退出动画
    1. exit
    2. exit-active
    3. exit-done
  • 还多了加载动画
    1. appear
    2. appear-active
    3. appear-done

2.3,常用 props

2.3.1,classNames

有2种情况:

  • 字符串,表示动画类名前缀(上面的例子已经演示了)。
  • 对象,设置状态对应的动画类名(所以可结合 animate.css 使用,下文有举例)。
js 复制代码
classNames={{
 appear: 'my-appear',
 appearActive: 'my-active-appear',
 appearDone: 'my-done-appear',
 enter: 'my-enter',
 enterActive: 'my-active-enter',
 enterDone: 'my-done-enter',
 exit: 'my-exit',
 exitActive: 'my-active-exit',
 exitDone: 'my-done-exit',
}}

2.3.2,appear

Transition 组件的 appear 属性中,介绍了它没有对应的状态来设置样式。而 CSSTransition 组件是有的。

举例:(只包含关键代码)

html 复制代码
<CSSTransition in={true} appear classNames="crane"></CSSTransition>
css 复制代码
.crane-appear {
    transform: translateX(200px);
}
.crane-appear-active {
    transform: translateX(0);
    transition: transform 200ms;
}
.crane-appear-done {
    transform: translateX(0);
}

这样,在页面加载完成时(刷新页面后),就会执行上面的动画了。

2.4,结合 animate.css

animate 样式举例1animate 样式举例2

安装:

bash 复制代码
npm install animate.css -S

样例完整代码:

js 复制代码
import { CSSTransition } from "react-transition-group";
import { useRef, useState } from "react";
import "./App.css";
import "animate.css";

function MyCSSTransition({ in: inProp, children }) {
    return (
        <div>
            <CSSTransition
                in={inProp}
                appear
                mountOnEnter
                timeout={1000}
                classNames={{
                    appearActive: "animate__fadeInRight",
                    enterActive: "animate__fadeInRight",
                    exitActive: "animate__fadeOutLeft",
                    exitDone: "exit-done",
                }}
            >
                <div className="animate__animated common">
                    {children}
                </div>
            </CSSTransition>
        </div>
    );
}

export default function App() {
    const [inProp, setInProp] = useState(true);
    return (
        <div className="app-box">
            <MyCSSTransition in={inProp}>
                <h1>组件1</h1>
            </MyCSSTransition>
            <MyCSSTransition in={!inProp}>
                <h1>组件2</h1>
            </MyCSSTransition>
            <button type="button" onClick={() => setInProp(!inProp)}>
                进入/退出
            </button>
        </div>
    );
}
css 复制代码
/* App.css */
.app-box {
    position: relative;
    margin-left: 80px;
    padding-top: 100px;
    width: 200px;
}
.common {
    position: absolute;
    top: 0;
}

.exit-done {
    display: none;
}

效果:

注意点:

1,在引入 animate.css 后,对要进行动画的元素的添加基础类名animate__animated,其他过渡动画添加 animate__动画类名即可。比如:

html 复制代码
<h1 class="animate__animated animate__bounce">An animated element</h1>

2,classNames 属性的类名最终会添加到 ref={nodeRef} 的元素上。

3,因为 animate.css 中的类名都是过渡动画的类名,所以只需要设置 appearActiveenterActiveexitActive这3个进行中的状态类名+最终态类名即可。

4,注意也设置了 mountOnEnter 属性,这是为了让没有进行动画的元素先不加载,以免影响到进行加载动画 appearActive 的元素。

接下篇文章 React 动画(下)


以上。

相关推荐
m0_748247551 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255022 小时前
前端常用算法集合
前端·算法
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203982 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2342 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1233 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~4 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语4 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport4 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg4 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全