import React, { useMemo, useRef, useEffect } from 'react'; import { StyleSheet, Text, View, ViewStyle, Animated } from 'react-native'; import { HourlyStepData } from '@/utils/health'; // 使用原生View来替代SVG,避免导入问题 // import Svg, { Rect } from 'react-native-svg'; interface StepsCardProps { stepCount: number | null; stepGoal: number; hourlySteps: HourlyStepData[]; style?: ViewStyle; } const StepsCard: React.FC = ({ stepCount, stepGoal, hourlySteps, style }) => { // 为每个柱体创建独立的动画值 const animatedValues = useRef( Array.from({ length: 24 }, () => new Animated.Value(0)) ).current; // 计算柱状图数据 const chartData = useMemo(() => { if (!hourlySteps || hourlySteps.length === 0) { return Array.from({ length: 24 }, (_, i) => ({ hour: i, steps: 0, height: 0 })); } // 找到最大步数用于计算高度比例 const maxSteps = Math.max(...hourlySteps.map(data => data.steps), 1); const maxHeight = 20; // 柱状图最大高度(缩小一半) return hourlySteps.map(data => ({ ...data, height: maxSteps > 0 ? (data.steps / maxSteps) * maxHeight : 0 })); }, [hourlySteps]); // 获取当前小时 const currentHour = new Date().getHours(); // 触发柱体动画 useEffect(() => { if (chartData && chartData.length > 0) { // 重置所有动画值 animatedValues.forEach(animValue => animValue.setValue(0)); // 同时启动所有柱体的弹性动画,有步数的柱体才执行动画 chartData.forEach((data, index) => { if (data.steps > 0) { Animated.spring(animatedValues[index], { toValue: 1, tension: 150, friction: 8, useNativeDriver: false, }).start(); } }); } }, [chartData, animatedValues]); return ( {/* 标题和步数显示 */} 步数 {/* 柱状图 */} {chartData.map((data, index) => { // 判断是否是当前小时或者有活动的小时 const isActive = data.steps > 0; const isCurrent = index <= currentHour; // 动画变换:缩放从0到实际高度 const animatedScale = animatedValues[index].interpolate({ inputRange: [0, 1], outputRange: [0, 1], }); // 动画变换:透明度从0到1 const animatedOpacity = animatedValues[index].interpolate({ inputRange: [0, 1], outputRange: [0, 1], }); return ( ); })} {/* 步数和目标显示 */} {stepCount !== null ? stepCount.toLocaleString() : '——'} ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'space-between', borderRadius: 20, padding: 16, shadowColor: '#000', shadowOffset: { width: 0, height: 4, }, shadowOpacity: 0.08, shadowRadius: 20, elevation: 8, }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, title: { fontSize: 14, color: '#192126', }, footprintIcons: { flexDirection: 'row', alignItems: 'center', gap: 6, }, chartContainer: { flex: 1, justifyContent: 'center', }, chartWrapper: { width: '100%', alignItems: 'center', }, chartArea: { flexDirection: 'row', alignItems: 'flex-end', height: 20, width: '100%', maxWidth: 240, justifyContent: 'space-between', paddingHorizontal: 4, }, chartBar: { width: 4, borderRadius: 1, alignSelf: 'flex-end', }, statsContainer: { alignItems: 'flex-start', marginTop: 6 }, stepCount: { fontSize: 18, fontWeight: '600', color: '#192126', }, }); export default StepsCard;