目录
- 3,SwitchTransition
- 4,TransitionGroup
接上面文章 React动画(上)
3,SwitchTransition
在上篇文章中,用 CSSTransition 做了一个动画:
可以看到,这2个元素的动画是同时发生的,因为使用同一个状态变量控制的(代码参考上篇文章)
而 SwitchTransition 组件是专门用来做切换动画 的,并且2个元素的动画是有先后顺序的。
元素1完成退出动画后,元素2再开始进入动画。
3.1,原理
默认情况下(mode="out-in"
),通过改变状态变量来改变 key 值,才能开始切换动画,
key
属性替换了之前的in
属性。
如果默认 state=true
,渲染元素1,则切换状态时的动画过程:
- 先给元素1添加类名
exit exit-active
,元素1执行退出动画。 - 等元素1的退出动画完成(过渡时间结束),再给元素2添加类名
enter enter-active
执行进入动画。 - 等元素2的进入动画完成,类名切换为
enter-done
。
所以,只需要设置这5个类名对应的样式即可。
如果进入动画结束后,用的默认样式(比如:
opacity: 1
)那类名enter-done
也不用设置。
3.1.2,key
因为会将退出动画和进入动画对应的类名,设置到对应的元素上,来实现切换动画。
所以如果不设置,那所有的类名会加到同一个元素上,结果是该元素无限进入循环动画(退出-进入-退出...)
key 只要保证唯一,能区分为2个元素即可。
3.1.2,mode
默认 out-in
,具体动画过程上面已经介绍了。
而 in-out
,如果默认 state=true
,渲染元素1,则切换状态时的动画过程:
- 保持元素1不变,为元素2添加
enter enter-active
执行进入动画。 - 过渡时间结束,元素2的类名变为
enter-done
,同时为元素1添加类名exit exit-active
,开始退出动画。 - 过渡时间结束,元素1被移除。
相当于先执行了一次元素2的进入动画,之后再开始执行元素1的退出动画。
3.2,举例
js
import { CSSTransition, SwitchTransition } from "react-transition-group";
import { useRef, useState } from "react";
import "./App.css";
export default function App() {
const [state, setState] = useState(true);
return (
<SwitchTransition>
<CSSTransition key={state} timeout={500}>
<button onClick={() => setState(!state)}>{state ? "状态1" : "状态2"}</button>
</CSSTransition>
</SwitchTransition>
);
}
css
/* App.css */
.enter {
opacity: 0;
}
.enter-active {
opacity: 1;
transition: opacity 1s;
}
.exit {
opacity: 1;
}
.exit-active {
opacity: 0;
transition: opacity 1s;
}
效果:
同样的,要在过渡完成后执行 transitionend
事件,可以:
js
export default function App() {
const [state, setState] = useState(true);
const refBtn1 = useRef(null);
const refBtn2 = useRef(null);
const nodeRef = state ? refBtn1 : refBtn2;
return (
<SwitchTransition>
<CSSTransition
key={state}
timeout={1000}
nodeRef={nodeRef}
addEndListener={() => {
nodeRef.current.addEventListener(
"transitionend",
() => {
console.log("过渡结束");
},
{ once: true }
);
}}
>
<button ref={nodeRef} onClick={() => setState((state) => !state)}>
{state ? "状态1" : "状态2"}
</button>
</CSSTransition>
</SwitchTransition>
);
}
3.3,结合 animate.css
安装:
bash
npm install animate.css -S
样例完整代码:
js
import { CSSTransition, SwitchTransition } from "react-transition-group";
import { useState } from "react";
import "animate.css";
export default function App() {
const [state, setState] = useState(true);
return (
<SwitchTransition>
<CSSTransition
key={state}
timeout={1000}
classNames={{
appearActive: "animate__fadeInRight",
enterActive: "animate__fadeInRight",
exitActive: "animate__fadeOutLeft",
}}
>
<button className="animate__animated" onClick={() => setState(!state)}>
{state ? "状态1" : "状态2"}
</button>
</CSSTransition>
</SwitchTransition>
);
}
效果:
4,TransitionGroup
它接收一组 CSSTransition 或 Transition,统一控制它们的进入和退出动画。
和 SwitchTransition 一样,会添加对应的类名来实现动画。
经典例子:待办列表,可以动态新增或删除待办项,同时为每一项都应用动画。
js
import { TransitionGroup, CSSTransition } from "react-transition-group";
import { useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./App.css";
export default function App() {
const [todoList, setTodoList] = useState([
{ id: uuidv4(), content: "学习" },
{ id: uuidv4(), content: "吃饭" },
{ id: uuidv4(), content: "睡觉" },
]);
return (
<>
<TransitionGroup>
{todoList.map((item) => (
<CSSTransition key={item.id} timeout={500}>
<div>
<span>{item.content}</span>
<button
onClick={() => {
const resultItem = todoList.filter((f) => f.id !== item.id);
setTodoList(resultItem);
}}
>
删除
</button>
</div>
</CSSTransition>
))}
</TransitionGroup>
<button
onClick={() => {
const answer = window.prompt();
setTodoList([
...todoList,
{
id: uuidv4(),
content: answer,
},
]);
}}
>
新增
</button>
</>
);
}
4.1,其他属性
4.1.2,appear
统一添加加载动画,同时会有 appear
,appear-active
,appear-done
类名。
4.1.2,component
控制渲染的容器的节点类名,默认 div
,null
表示不渲染节点。
html
<TransitionGroup appear component="ul">
4.1.3,classNames
是 component
对应节点的类名,不是动画类名前缀。
动画类名前缀,还是要添加到 CSSTransition 组件上。
以上。