解决的问题
1.动画未完成页面数字滚动,图表开始渲染 2.统一管理页面进场,出场. 3.出场动画时间不一样
TransitionWrap
TransitionPage的容器. TransitionWrap会获取子孙TransitionPage组件,value值等于TransitionPage的keys时渲染页面
属性 | 说明 | 类型 | 默认 |
---|---|---|---|
value | 渲染哪一个transitionPage页面 | string | - |
inEndFn | 整个页面进场动画结束回调 | Function | - |
outEndFn | 整个页面出场动画结束回调 | Function | - |
TransitionPage
页面容器
属性 | 说明 | 类型 | 默认 |
---|---|---|---|
page | 页面dom | dom | - |
keys | 页面标识 | string | - |
Transition
在show改变时会给Transition添加不同的类名
属性 | 说明 | 类型 | 默认 |
---|---|---|---|
name | 类名前缀 | string | default |
className | 类名 | class | - |
style | 样式 | css | - |
inEndFn | 进场动画结束回调函数 | Function | - |
outEndFn | 出场动画结束回调函数 | Function | - |
过渡的类名
1.[name]-in: 在元素被插入之时生效,在元素被插入之后的下一帧移除。 2.[name]-in-active: 在元素被插入时生效 3.[name]-out-to: 在show改为false生效 4.[name]-out-active: 在show改为false生效
示例
项目入口文件 page/index.tsx
import { useState } from 'react';
import { TransitionWrap, TransitionPage } from '@/components/transition'; // 引入组件容器
import PageA from './PageA';
import PageB from './PageB';
const IndexPage = () => {
const [pageName, setPageName] = useState('PageA'); // 当前页面
// 出场动画结束回调
let handlePage = () => {
console.log('出场动画结束回调')
};
// 接收到中控发来消息
const API_PageChange = (data: any) => {
setPageName(data.page)
}
return (
<TransitionWrap value={pageName} outEndFn={handlePage}>
<button onClick={() => API_PageChange({ page: 'PageA' })}>切换到: PageA</button>
<button onClick={() => API_PageChange({ page: 'PageB' })}>切换到: PageB</button>
<TransitionPage keys="PageA" page={<PageA />} />
<TransitionPage keys="PageB" page={<PageB />} />
</TransitionWrap>
);
}
export default IndexPage;
pageA页面中
import styles from './index.less';
import { Transition, TransitionWrap, TransitionPage } from '@/components/transition'; // 引入过渡组件
import CountUp from '@/components/CountUp';
import { useState } from 'react';
const pageA = () => {
const [pageName, setPageName] = useState('right1'); // 当前页面
// 接收到中控发来消息
const API_PageChange = (data: any) => {
setPageName(data.page)
}
return <div>
<button onClick={() => API_PageChange({ page: 'right1' })}>切换到: right1</button>
<button onClick={() => API_PageChange({ page: 'right2' })}>切换到: right2</button>
<div className={styles.page}>
<Transition name="left" className={styles.left} inEndFn={() => {console.log('进场动画结束')}}>
name 为 left
<CountUp end={100} />
</Transition>
<Transition name="center">
<h1>PageA</h1>
<h1>name 为 center</h1>
</Transition>
<TransitionWrap value={pageName}>
<TransitionPage keys="right1" page={
<Transition name="right" className={styles.right}>
name 为 right1
page 为 1
<CountUp end={100} />
</Transition>
} />
<TransitionPage keys="right2" page={
<Transition name="right" className={styles.right}>
name 为 right
page 为 2
<CountUp end={200} />
</Transition>
} />
</TransitionWrap>
</div>
</div>
}
export default pageA;
// ---------------------styles---------------------
.page {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: 40px;
box-sizing: border-box;
.left {
box-sizing: border-box;
width: 200px;
height: 400px;
background-color: #f00;
}
.right {
box-sizing: border-box;
width: 200px;
height: 400px;
background-color: #f0f;
}
}
pageB页面中
import styles from './index.less';
import { Transition } from '@/components/transition'; // 引入过渡组件
import CountUp from '@/components/CountUp';
const pageB = () => {
return <div className={styles.page}>
<Transition name="left2" className={styles.left} inEndFn={() => {console.log('进场动画结束')}}>
name 为 left2
<CountUp end={100} />
</Transition>
<Transition name="center2">
<h1>PageB</h1>
<h1>name 为 center2</h1>
</Transition>
<Transition name="right2" className={styles.right}>
name 为 right2
</Transition>
</div>
}
export default pageB;
// ---------------------styles---------------------
.page {
display: flex;
align-items: baseline;
justify-content: space-between;
padding: 40px;
box-sizing: border-box;
.left {
box-sizing: border-box;
width: 200px;
height: 400px;
background-color: #f00;
}
.right {
box-sizing: border-box;
width: 200px;
height: 400px;
background-color: #f0f;
}
}
此时的Transition的css为:
.left-in, .left-out-to {
transform: translateX(-150%);
}
.left-in-active, .left-out-active {
transition: transform 1.8s;
}
.right-in, .right-out-to {
transform: translateX(150%);
}
.right-in-active, .right-out-active {
transition: transform .8s;
}
.center-in-active {
animation: centerIn .8s linear;
}
.center-out-active {
animation: centerOut .8s linear forwards;
}
@keyframes centerIn {
0% { transform: scale(0) }
100% { transform: scale(1); }
}
@keyframes centerOut {
0% { transform: scale(1) }
100% { transform: scale(0); }
}
.left2-in-active {
animation: left2In .8s linear;
}
@keyframes left2In {
0% {
transform: translateX(-150%);
height: 20px;
overflow: hidden;
}
40% {
transform: translateX(0%);
height: 20px;
overflow: hidden;
}
100% {
transform: translateX(0%);
height: 400px;
overflow: hidden;
}
}
.left2-out-active {
animation: left2Out .8s linear;
}
@keyframes left2Out {
0% {
transform: translateX(0%);
height: 400px;
overflow: hidden;
}
40% {
transform: translateX(0%);
height: 20px;
overflow: hidden;
}
100% {
transform: translateX(-150%);
height: 20px;
overflow: hidden;
}
}
.center2-in, .center2-out-to {
transform: translateY(-250%);
}
.center2-in-active, .center2-out-active {
transition: transform .8s;
}
.right2-in-active {
animation: right2In .8s linear;
}
@keyframes right2In {
0% {
opacity: 0;
border-radius: 100%;
width: 0;
height: 0;
overflow: hidden;
}
100% {
opacity: 1;
border-radius: 0%;
overflow: hidden;
}
}
.right2-out-active {
animation: right2Out .8s linear;
}
@keyframes right2Out {
0% {
border-radius: 0%;
overflow: hidden;
}
100% {
width: 0;
height: 0;
opacity: 0;
border-radius: 100%;
overflow: hidden;
}
}
.left-in, .left-out-to {
transform: translateX(-110%);
}
.left-in-active, .left-out-active {
transition: transform .8s;
}
.right-in, .right-out-to {
transform: translateX(110%);
}
.right-in-active, .right-out-active {
transition: transform .8s;
}
Transition下的组件获取页面进场结束 如 CountUp 组件
import ReactCountUp from 'react-countup';
import { CountUpProps } from 'react-countup/build/CountUp';
import { useTransitionEnd } from '@/components/transition'; // 页面进场结束use
import { useState } from 'react';
const CountUp: React.FC<CountUpProps> = (props) => {
const [flag, setFlag] = useState(false);
useTransitionEnd(() => { setFlag(true); console.log('进场动画结束') });
return <ReactCountUp duration={2.75} {...props} end={!flag ? 0 : props.end}></ReactCountUp>;
};
export default CountUp;