diff --git a/app/(tabs)/statistics.tsx b/app/(tabs)/statistics.tsx index b4046f2..3daf22c 100644 --- a/app/(tabs)/statistics.tsx +++ b/app/(tabs)/statistics.tsx @@ -26,6 +26,7 @@ import dayjs from 'dayjs'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import { + AppState, SafeAreaView, ScrollView, StyleSheet, @@ -240,20 +241,42 @@ export default function ExploreScreen() { } }; + // 加载所有数据的统一方法 + const loadAllData = React.useCallback((targetDate?: Date) => { + const dateToUse = targetDate || getCurrentSelectedDate(); + if (dateToUse) { + loadHealthData(dateToUse); + if (isLoggedIn) { + loadNutritionData(dateToUse); + loadMoodData(dateToUse); + } + } + }, [isLoggedIn]); + useFocusEffect( React.useCallback(() => { - // 聚焦时按当前选中的日期加载,避免与用户手动选择的日期不一致 - const currentDate = currentSelectedDate; - if (currentDate) { - loadHealthData(currentDate); - if (isLoggedIn) { - loadNutritionData(currentDate); - loadMoodData(currentDate); - } - } - }, [selectedIndex]) + // 每次聚焦时都拉取当前选中日期的最新数据 + loadAllData(); + }, [loadAllData]) ); + // AppState 监听:应用从后台返回前台时刷新数据 + useEffect(() => { + const handleAppStateChange = (nextAppState: string) => { + if (nextAppState === 'active') { + // 应用从后台返回前台,刷新当前选中日期的数据 + console.log('应用从后台返回前台,刷新统计数据...'); + loadAllData(); + } + }; + + const subscription = AppState.addEventListener('change', handleAppStateChange); + + return () => { + subscription?.remove(); + }; + }, [loadAllData]); + useEffect(() => { // 注册任务 registerTask({ @@ -272,11 +295,7 @@ export default function ExploreScreen() { // 日期点击时,加载对应日期数据 const onSelectDate = (index: number, date: Date) => { setSelectedIndex(index); - loadHealthData(date); - if (isLoggedIn) { - loadNutritionData(date); - loadMoodData(date); - } + loadAllData(date); }; diff --git a/components/StepsCard.tsx b/components/StepsCard.tsx index d73b357..cec50e8 100644 --- a/components/StepsCard.tsx +++ b/components/StepsCard.tsx @@ -1,9 +1,10 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useRef, useEffect } from 'react'; import { StyleSheet, Text, View, - ViewStyle + ViewStyle, + Animated } from 'react-native'; import { HourlyStepData } from '@/utils/health'; @@ -23,6 +24,11 @@ const StepsCard: React.FC = ({ hourlySteps, style }) => { + // 为每个柱体创建独立的动画值 + const animatedValues = useRef( + Array.from({ length: 24 }, () => new Animated.Value(0)) + ).current; + // 计算柱状图数据 const chartData = useMemo(() => { if (!hourlySteps || hourlySteps.length === 0) { @@ -42,6 +48,26 @@ const StepsCard: React.FC = ({ // 获取当前小时 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 ( {/* 标题和步数显示 */} @@ -58,14 +84,28 @@ const StepsCard: React.FC = ({ 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 ( -