Files
digital-pilates/components/AnimatedNumber.tsx
2025-09-02 15:50:35 +08:00

83 lines
2.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useRef, useState } from 'react';
import { Animated, Easing, TextStyle } from 'react-native';
type AnimatedNumberProps = {
value: number; // 最终值
durationMs?: number;
format?: (v: number) => string;
style?: TextStyle;
/** 当该值变化时从0重新动画 */
resetToken?: unknown;
};
export function AnimatedNumber({
value,
durationMs = 300,
format,
style,
resetToken,
}: AnimatedNumberProps) {
const opacity = useRef(new Animated.Value(1)).current;
const [display, setDisplay] = useState<string>('0');
const [currentValue, setCurrentValue] = useState(0);
useEffect(() => {
// 如果值没有变化,不执行动画
if (value === currentValue && resetToken === undefined) {
return;
}
// 停止当前动画
opacity.stopAnimation(() => {
// 创建优雅的透明度变化动画
const fadeOut = Animated.timing(opacity, {
toValue: 0.2, // 淡出到较低透明度
duration: durationMs * 0.4, // 淡出占总时长的40%
easing: Easing.out(Easing.quad),
useNativeDriver: false,
});
const fadeIn = Animated.timing(opacity, {
toValue: 1,
duration: durationMs * 0.6, // 淡入占总时长的60%
easing: Easing.out(Easing.quad),
useNativeDriver: false,
});
// 在淡出完成时更新数字显示
fadeOut.start(() => {
// 更新当前值和显示
setCurrentValue(value);
setDisplay(format ? format(value) : `${Math.round(value)}`);
// 然后淡入新数字
fadeIn.start();
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value, resetToken]);
// 初始化显示值
useEffect(() => {
if (currentValue !== value) {
setCurrentValue(value);
setDisplay(format ? format(value) : `${Math.round(value)}`);
}
}, [value, format, currentValue]);
return (
<Animated.Text
style={[
style,
{
opacity: opacity
}
]}
>
{display}
</Animated.Text>
);
}