feat: 优化营养记录和卡路里环图组件,增加毛玻璃背景和动画效果
This commit is contained in:
@@ -11,21 +11,18 @@ export type CalorieRingChartProps = {
|
||||
metabolism: number;
|
||||
exercise: number;
|
||||
consumed: number;
|
||||
goal: number;
|
||||
protein: number;
|
||||
fat: number;
|
||||
carbs: number;
|
||||
proteinGoal: number;
|
||||
fatGoal: number;
|
||||
carbsGoal: number;
|
||||
|
||||
};
|
||||
|
||||
export function CalorieRingChart({
|
||||
metabolism,
|
||||
exercise,
|
||||
consumed,
|
||||
goal,
|
||||
protein,
|
||||
fat,
|
||||
carbs,
|
||||
@@ -49,9 +46,9 @@ export function CalorieRingChart({
|
||||
const totalAvailable = metabolism + exercise;
|
||||
const progressPercentage = totalAvailable > 0 ? Math.min((consumed / totalAvailable) * 100, 100) : 0;
|
||||
|
||||
// 圆环参数 - 更小的圆环以适应布局
|
||||
const radius = 62;
|
||||
const strokeWidth = 6;
|
||||
// 圆环参数 - 减小尺寸以优化空间占用
|
||||
const radius = 48;
|
||||
const strokeWidth = 8; // 增加圆环厚度
|
||||
const center = radius + strokeWidth;
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const strokeDasharray = circumference;
|
||||
@@ -100,7 +97,7 @@ export function CalorieRingChart({
|
||||
cx={center}
|
||||
cy={center}
|
||||
r={radius}
|
||||
stroke={progressPercentage > 80 ? "#FF6B6B" : "#E0E0E0"}
|
||||
stroke={progressPercentage > 80 ? "#FF6B6B" : "#4ECDC4"}
|
||||
strokeWidth={strokeWidth}
|
||||
fill="none"
|
||||
strokeDasharray={`${strokeDasharray}`}
|
||||
@@ -116,7 +113,7 @@ export function CalorieRingChart({
|
||||
还能吃
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.centerValue, { color: textColor }]}>
|
||||
{canEat.toFixed(1)}千卡
|
||||
{Math.round(canEat)}千卡
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.centerPercentage, { color: textSecondaryColor }]}>
|
||||
{Math.round(progressPercentage)}%
|
||||
@@ -126,59 +123,58 @@ export function CalorieRingChart({
|
||||
|
||||
{/* 右侧数据展示 */}
|
||||
<View style={styles.dataContainer}>
|
||||
{/* 各项数值 */}
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>代谢</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(metabolism)}千卡
|
||||
</ThemedText>
|
||||
<View style={styles.dataBackground}>
|
||||
{/* 左右两列布局 */}
|
||||
<View style={styles.dataColumns}>
|
||||
{/* 左列:卡路里数据 */}
|
||||
<View style={styles.dataColumn}>
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>代谢</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(metabolism)}千卡
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>运动</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(exercise)}千卡
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>饮食</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(consumed)}千卡
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 右列:营养数据 */}
|
||||
<View style={styles.dataColumn}>
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>蛋白质</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(protein)}g
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>脂肪</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(fat)}g
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>碳水</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(carbs)}g
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>运动</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(exercise)}千卡
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<View style={styles.dataItem}>
|
||||
<ThemedText style={[styles.dataLabel, { color: textSecondaryColor }]}>饮食</ThemedText>
|
||||
<ThemedText style={[styles.dataValue, { color: textColor }]}>
|
||||
{Math.round(consumed)}千卡
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 底部营养素展示 */}
|
||||
<View style={styles.nutritionContainer}>
|
||||
<View style={styles.nutritionItem}>
|
||||
<ThemedText style={[styles.nutritionLabel, { color: textSecondaryColor }]}>
|
||||
蛋白质
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.nutritionValue, { color: textColor }]}>
|
||||
{Math.round(protein)}/{Math.round(proteinGoal)}g
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<View style={styles.nutritionItem}>
|
||||
<ThemedText style={[styles.nutritionLabel, { color: textSecondaryColor }]}>
|
||||
脂肪
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.nutritionValue, { color: textColor }]}>
|
||||
{Math.round(fat)}/{Math.round(fatGoal)}g
|
||||
</ThemedText>
|
||||
</View>
|
||||
|
||||
<View style={styles.nutritionItem}>
|
||||
<ThemedText style={[styles.nutritionLabel, { color: textSecondaryColor }]}>
|
||||
碳水化合物
|
||||
</ThemedText>
|
||||
<ThemedText style={[styles.nutritionValue, { color: textColor }]}>
|
||||
{Math.round(carbs)}/{Math.round(carbsGoal)}g
|
||||
</ThemedText>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -213,14 +209,14 @@ const styles = StyleSheet.create({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 16,
|
||||
marginBottom: 0, // 移除底部间距,因为不再有底部营养容器
|
||||
paddingHorizontal: 8,
|
||||
},
|
||||
chartContainer: {
|
||||
position: 'relative',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 140,
|
||||
width: 112, // 减少宽度以匹配更小的圆环 (48*2 + 8*2)
|
||||
flexShrink: 0,
|
||||
},
|
||||
centerContent: {
|
||||
@@ -235,8 +231,8 @@ const styles = StyleSheet.create({
|
||||
marginBottom: 2,
|
||||
},
|
||||
centerValue: {
|
||||
fontSize: 16,
|
||||
fontWeight: '700',
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#333333',
|
||||
marginBottom: 1,
|
||||
},
|
||||
@@ -247,15 +243,29 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
dataContainer: {
|
||||
flex: 1,
|
||||
marginLeft: 32,
|
||||
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,
|
||||
paddingLeft: 8,
|
||||
},
|
||||
dataItem: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
dataIcon: {
|
||||
width: 6,
|
||||
@@ -273,26 +283,13 @@ const styles = StyleSheet.create({
|
||||
fontWeight: '600',
|
||||
color: '#333333',
|
||||
},
|
||||
nutritionContainer: {
|
||||
dataColumns: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
paddingTop: 12,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: 'rgba(0,0,0,0.06)',
|
||||
gap: 12,
|
||||
},
|
||||
nutritionItem: {
|
||||
alignItems: 'center',
|
||||
dataColumn: {
|
||||
flex: 1,
|
||||
},
|
||||
nutritionLabel: {
|
||||
fontSize: 10,
|
||||
fontWeight: '500',
|
||||
color: '#999999',
|
||||
marginBottom: 3,
|
||||
},
|
||||
nutritionValue: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
color: '#333333',
|
||||
gap: 4,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user