?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秒調用一次APIuseEffect(() => {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;