import { ThemedText } from '@/components/ThemedText'; import { useThemeColor } from '@/hooks/useThemeColor'; import React, { useEffect, useRef } from 'react'; import { Animated, StyleSheet, View } from 'react-native'; import Svg, { Circle } from 'react-native-svg'; const AnimatedCircle = Animated.createAnimatedComponent(Circle); export type CalorieRingChartProps = { metabolism: number; exercise: number; consumed: number; protein: number; fat: number; carbs: number; proteinGoal: number; fatGoal: number; carbsGoal: number; }; export function CalorieRingChart({ metabolism, exercise, consumed, protein, fat, carbs, proteinGoal, fatGoal, carbsGoal, }: CalorieRingChartProps) { const surfaceColor = useThemeColor({}, 'surface'); const textColor = useThemeColor({}, 'text'); const textSecondaryColor = useThemeColor({}, 'textSecondary'); // 动画值 const animatedProgress = useRef(new Animated.Value(0)).current; // 计算还能吃的卡路里:代谢 + 运动 - 饮食 const remainingCalories = metabolism + exercise - consumed; const canEat = Math.max(0, remainingCalories); // 计算进度百分比 (用于圆环显示) const totalAvailable = metabolism + exercise; const progressPercentage = totalAvailable > 0 ? Math.min((consumed / totalAvailable) * 100, 100) : 0; // 圆环参数 - 减小尺寸以优化空间占用 const radius = 48; const strokeWidth = 8; // 增加圆环厚度 const center = radius + strokeWidth; const circumference = 2 * Math.PI * radius; const strokeDasharray = circumference; // 动画效果 useEffect(() => { Animated.timing(animatedProgress, { toValue: progressPercentage, duration: 600, useNativeDriver: false, }).start(); }, [progressPercentage]); // 使用动画值计算strokeDashoffset const strokeDashoffset = animatedProgress.interpolate({ inputRange: [0, 100], outputRange: [circumference, 0], extrapolate: 'clamp', }); return ( {/* 左上角公式展示 */} 还能吃 = 代谢 + 运动 - 饮食 {/* 主要内容区域 */} {/* 左侧圆环图 */} {/* 背景圆环 */} {/* 进度圆环 - 保持固定颜色 */} 80 ? "#FF6B6B" : "#4ECDC4"} strokeWidth={strokeWidth} fill="none" strokeDasharray={`${strokeDasharray}`} strokeDashoffset={strokeDashoffset} strokeLinecap="round" transform={`rotate(-90 ${center} ${center})`} /> {/* 中心内容 */} 还能吃 {Math.round(canEat)}千卡 {/* 右侧数据展示 */} {/* 左右两列布局 */} {/* 左列:卡路里数据 */} 代谢 {Math.round(metabolism)}千卡 运动 {Math.round(exercise)}千卡 饮食 {Math.round(consumed)}千卡 {/* 右列:营养数据 */} 蛋白质 {Math.round(protein)}g 脂肪 {Math.round(fat)}g 碳水 {Math.round(carbs)}g ); } const styles = StyleSheet.create({ container: { backgroundColor: '#FFFFFF', borderRadius: 16, padding: 16, marginHorizontal: 16, marginBottom: 8, shadowColor: '#000000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.04, shadowRadius: 8, elevation: 2, }, formulaContainer: { alignItems: 'flex-start', marginBottom: 12, }, formulaText: { fontSize: 12, fontWeight: '500', color: '#999999', lineHeight: 16, }, mainContent: { width: '100%', flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 0, // 移除底部间距,因为不再有底部营养容器 paddingHorizontal: 8, }, chartContainer: { position: 'relative', alignItems: 'center', justifyContent: 'center', width: 112, // 减少宽度以匹配更小的圆环 (48*2 + 8*2) flexShrink: 0, }, centerContent: { position: 'absolute', alignItems: 'center', justifyContent: 'center', }, centerLabel: { fontSize: 11, fontWeight: '500', color: '#999999', marginBottom: 2, }, centerValue: { fontSize: 14, fontWeight: '600', color: '#333333', marginBottom: 1, }, centerPercentage: { fontSize: 11, fontWeight: '500', color: '#999999', }, dataContainer: { flex: 1, marginLeft: 16, }, dataBackground: { backgroundColor: 'rgba(248, 250, 252, 0.8)', // 毛玻璃背景色 borderRadius: 12, padding: 12, shadowColor: '#000', shadowOffset: { width: 0, height: 1, }, shadowOpacity: 0.06, shadowRadius: 3, elevation: 1, // 添加边框增强毛玻璃效果 borderWidth: 0.5, borderColor: 'rgba(255, 255, 255, 0.8)', gap: 4, }, dataItem: { flexDirection: 'row', alignItems: 'center', gap: 4, }, dataIcon: { width: 6, height: 6, borderRadius: 3, }, dataLabel: { fontSize: 11, fontWeight: '500', color: '#999999', minWidth: 28, }, dataValue: { fontSize: 11, fontWeight: '600', color: '#333333', }, dataColumns: { flexDirection: 'row', justifyContent: 'space-between', gap: 12, }, dataColumn: { flex: 1, gap: 4, }, });