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 动画(下)


以上。

相关推荐
GIS之路7 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug10 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213812 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中34 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路37 分钟前
GDAL 实现矢量合并
前端
hxjhnct40 分钟前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星1 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端