react + ant-design实现数字对比动画效果:当新获取的数字比之前展示的数字多或少2时,显示“+2”或“-2”的动画效果
1. 创建独立的 AnimatedValue
组件
// components/AnimatedValue/index.jsx
import React, { useState, useEffect, useRef } from 'react';
import styles from './styles.module.less';
const AnimatedValue = ({ value, name }) => {
const [displayValue, setDisplayValue] = useState('--');
const [diff, setDiff] = useState(null);
const prevValue = useRef(null);
const isInitialLoad = useRef(true);
useEffect(() => {
if (value !== undefined && value !== null) {
const numValue = Number(value);
// 只有在不是初始加载且前值存在时才计算差值
if (!isInitialLoad.current && prevValue.current !== null) {
const difference = numValue - prevValue.current;
if (Math.abs(difference) >= 1) {
setDiff(difference);
// 3秒后清除差值显示
const timer = setTimeout(() => setDiff(null), 3000);
return () => clearTimeout(timer);
}
}
prevValue.current = numValue;
setDisplayValue(numValue);
if (isInitialLoad.current) {
isInitialLoad.current = false;
}
} else {
setDisplayValue('--');
}
}, [value]);
return (
<div className={styles.value_container}>
<div className={styles.value}>{displayValue}</div>
{diff !== null && (
<div className={`${styles.diff_animation} ${
diff > 0 ? styles.increase : styles.decrease
}`}>
{diff > 0 ? `+${diff}` : diff}
</div>
)}
</div>
);
};
export default AnimatedValue;
2. 对应的样式文件
// components/AnimatedValue/styles.module.less
.value_container {
position: relative;
display: inline-block;
min-width: 60px;
height: 30px; // 固定高度防止布局抖动
}
.value {
display: inline-block;
}
.diff_animation {
position: absolute;
right: -30px;
top: 0;
font-size: 14px;
font-weight: bold;
animation: fadeUp 1.5s ease-out forwards;
will-change: transform, opacity;
&.increase {
color: #f5222d; // 红色表示增加
}
&.decrease {
color: #52c41a; // 绿色表示减少
}
}
@keyframes fadeUp {
0% {
opacity: 1;
transform: translateY(0);
}
70% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(-20px);
}
}
3. 在父组件中使用
import React, { useState, useEffect } from 'react';
import { Row, Col } from 'antd';
import AnimatedValue from '@/components/AnimatedValue';
import styles from './yourStyles.module.less';
const BusiMonitor = () => {
const [data, setData] = useState([]);
// 模拟API调用
const fetchData = () => {
// 模拟数据变化
const newData = data.map(item => ({
...item,
value: Math.floor(Math.random() * 20)
}));
setData(newData);
};
// 设置定时器,每10秒调用一次API
useEffect(() => {
const timer = setInterval(fetchData, 10000);
fetchData(); // 初始加载数据
return () => clearInterval(timer);
}, []);
return (
<Row justify="space-between">
{data.map(({ name, value }, index) => (
<Col >
<div>
<div className={styles.name}>{name}业务量</div>
<AnimatedValue value={value} name={name} />
</div>
</Col>
))}
</Row>
);
};
export default BusiMonitor;