feat: 优化数据加载逻辑,添加应用状态监听以刷新统计数据;为步数卡片添加动画效果

This commit is contained in:
2025-08-30 23:07:14 +08:00
parent 6bdfda9fd3
commit 4bb0576d92
2 changed files with 77 additions and 18 deletions

View File

@@ -26,6 +26,7 @@ import dayjs from 'dayjs';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { import {
AppState,
SafeAreaView, SafeAreaView,
ScrollView, ScrollView,
StyleSheet, 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( useFocusEffect(
React.useCallback(() => { React.useCallback(() => {
// 聚焦时当前选中日期加载,避免与用户手动选择的日期不一致 // 每次聚焦时都拉取当前选中日期的最新数据
const currentDate = currentSelectedDate; loadAllData();
if (currentDate) { }, [loadAllData])
loadHealthData(currentDate);
if (isLoggedIn) {
loadNutritionData(currentDate);
loadMoodData(currentDate);
}
}
}, [selectedIndex])
); );
// AppState 监听:应用从后台返回前台时刷新数据
useEffect(() => {
const handleAppStateChange = (nextAppState: string) => {
if (nextAppState === 'active') {
// 应用从后台返回前台,刷新当前选中日期的数据
console.log('应用从后台返回前台,刷新统计数据...');
loadAllData();
}
};
const subscription = AppState.addEventListener('change', handleAppStateChange);
return () => {
subscription?.remove();
};
}, [loadAllData]);
useEffect(() => { useEffect(() => {
// 注册任务 // 注册任务
registerTask({ registerTask({
@@ -272,11 +295,7 @@ export default function ExploreScreen() {
// 日期点击时,加载对应日期数据 // 日期点击时,加载对应日期数据
const onSelectDate = (index: number, date: Date) => { const onSelectDate = (index: number, date: Date) => {
setSelectedIndex(index); setSelectedIndex(index);
loadHealthData(date); loadAllData(date);
if (isLoggedIn) {
loadNutritionData(date);
loadMoodData(date);
}
}; };

View File

@@ -1,9 +1,10 @@
import React, { useMemo } from 'react'; import React, { useMemo, useRef, useEffect } from 'react';
import { import {
StyleSheet, StyleSheet,
Text, Text,
View, View,
ViewStyle ViewStyle,
Animated
} from 'react-native'; } from 'react-native';
import { HourlyStepData } from '@/utils/health'; import { HourlyStepData } from '@/utils/health';
@@ -23,6 +24,11 @@ const StepsCard: React.FC<StepsCardProps> = ({
hourlySteps, hourlySteps,
style style
}) => { }) => {
// 为每个柱体创建独立的动画值
const animatedValues = useRef(
Array.from({ length: 24 }, () => new Animated.Value(0))
).current;
// 计算柱状图数据 // 计算柱状图数据
const chartData = useMemo(() => { const chartData = useMemo(() => {
if (!hourlySteps || hourlySteps.length === 0) { if (!hourlySteps || hourlySteps.length === 0) {
@@ -42,6 +48,26 @@ const StepsCard: React.FC<StepsCardProps> = ({
// 获取当前小时 // 获取当前小时
const currentHour = new Date().getHours(); 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 ( return (
<View style={[styles.container, style]}> <View style={[styles.container, style]}>
{/* 标题和步数显示 */} {/* 标题和步数显示 */}
@@ -58,14 +84,28 @@ const StepsCard: React.FC<StepsCardProps> = ({
const isActive = data.steps > 0; const isActive = data.steps > 0;
const isCurrent = index <= currentHour; 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 ( return (
<View <Animated.View
key={`bar-${index}`} key={`bar-${index}`}
style={[ style={[
styles.chartBar, styles.chartBar,
{ {
height: data.height || 2, // 最小高度2px height: data.height || 2, // 最小高度2px
backgroundColor: isCurrent && isActive ? '#FFC365' : '#FFEBCB', backgroundColor: isCurrent && isActive ? '#FFC365' : '#FFEBCB',
transform: [{ scaleY: animatedScale }],
opacity: animatedOpacity,
} }
]} ]}
/> />