使用 react-transition-group 来实现动画。总共有4个动画组件,覆盖大多应用场景。
过渡动画的原理,和 Vue的过渡动画 类似。分为2个阶段:
default
;entering
entered
entered
exiting
exited
注意,
进入前的初始状态和exited
是一个状态。
退出前的初始状态和entered
也是一个状态。
以一个渐入渐出动画举例,
.default {
transition: opacity 2s ease-in-out;
}
.default, .exiting, .exited{
opacity: 0;
}
.entering, .entered {
opacity: 1;
}
官方文档的例子:
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
来添加更多的样式:
<div className={state}>I'm a fade Transition!div>
延迟挂载。顾名思义,在进入动画开始前再加载要进行动画的元素。默认 false
立即加载。
比如,对渐入渐出动画来说,如果初始值 in={false}
,也就是说元素一开始是隐藏的。
mountOnEnter={false}
元素依然会被挂载到 DOM 中,等待动画开始。mountOnEnter
或 mountOnEnter={true}
则元素延迟挂载,一开始并不会挂载到DOM中,直到动画开始才挂载并进行动画。顾名思义,在退出动画结果后,是否在DOM中直接卸载动画元素。默认 false
不卸载。
默认情况下,在元素挂载阶段,如果 in={true}
则直接进入动画的最终状态,整个过程 Transition.children
只会调用一次,参数 state=entered
。
此时设置 appear
或 appear={true}
,则进入动画会经历完整3个阶段,函数也会运行 3 次。
注意,
Transition
组件中并没有提供appear
这个状态,所以也无法设置改状态下的样式。所以只是函数会运行3次而已。
如果想一开始就执行一次进入的过渡动画,得使用下面这个组件CSSTransition
。
还有其他的一些属性不多介绍了,比如设置过渡动画结束后的回调函数等。用到时参考官方文档即可。
state
暴露出来,通过它来设置样式。更复杂的动画需要用到 onEnter
等回调函数来实现。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>
);
}
.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种状态,不过直接对应到类名上了:
enter
,动画开始前的初始化类名。enter-active
,动画进行中的类名。enter-done
,动画结束后的类名。exit
exit-active
exit-done
appear
appear-active
appear-done
有2种情况:
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',
}}
在 Transition 组件的 appear
属性中,介绍了它没有对应的状态来设置样式。而 CSSTransition 组件是有的。
举例:(只包含关键代码)
<CSSTransition in={true} appear classNames="crane">CSSTransition>
.crane-appear {
transform: translateX(200px);
}
.crane-appear-active {
transform: translateX(0);
transition: transform 200ms;
}
.crane-appear-done {
transform: translateX(0);
}
这样,在页面加载完成时(刷新页面后),就会执行上面的动画了。
安装:
npm install animate.css -S
样例完整代码:
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>
);
}
/* 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__动画类名
即可。比如:
<h1 class="animate__animated animate__bounce">An animated elementh1>
2,classNames 属性的类名最终会添加到 ref={nodeRef}
的元素上。
3,因为 animate.css 中的类名都是过渡动画的类名,所以只需要设置 appearActive
、enterActive
、exitActive
这3个进行中的状态类名+最终态类名即可。
4,注意也设置了 mountOnEnter 属性,这是为了让没有进行动画的元素先不加载,以免影响到进行加载动画 appearActive
的元素。
接下篇文章 React 动画(下)
以上。