欢迎加入开源鸿蒙PC社区:
https://harmonypc.csdn.net/

atomgit仓库地址:https://atomgit.com/Math_teacher_fan/tanshuiyudanbaizhi

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、项目概述与设计理念

1.1 应用背景

随着健康意识的提升,越来越多的人开始关注饮食营养。碳水化合物和蛋白质是人体最重要的两种营养素,但它们的吸收方式和效果截然不同:

  • 碳水化合物:快速提供能量,约30分钟达到吸收峰值
  • 蛋白质:缓慢吸收,约2小时达到吸收峰值,需要分解为氨基酸

本应用旨在帮助用户理解这两种营养素的差异,做出更科学的饮食选择。

1.2 技术架构选型

技术方案 优势 适用场景
鸿蒙Electron 原生性能优秀,系统集成度高 需要与鸿蒙系统深度交互的应用
Web前端技术 开发效率高,跨平台能力强 快速迭代,界面复杂的应用
Canvas 图表绘制能力强,性能优秀 科学数据可视化
LocalStorage 数据持久化简单,无需后端支持 单机应用,数据量较小的场景

1.3 功能模块划分

┌─────────────────────────────────────────────────────┐
│                    功能模块架构                      │
├─────────────────────────────────────────────────────┤
│  ⚖️ 营养对比  │  🍎 食物数据库  │  📊 吸收分析        │
├─────────────────────────────────────────────────────┤
│  🔢 摄入计算  │  📋 营养报告  │                     │
└─────────────────────────────────────────────────────┘

二、核心代码实现详解

2.1 营养对比模块实现

营养对比是应用的核心功能,展示碳水与蛋白质的能量对比。

const CompareModule = {
    init() {
        this.updateComparison();
        this.updateEnergyChart();
    },
    
    updateComparison() {
        const stats = DataManager.getTodayStats();
        
        // 更新每日热量
        document.getElementById('dailyCalories').textContent = stats.calories;
        
        // 计算碳水和蛋白质的热量贡献
        const carbCalories = stats.carb * 4;      // 碳水:4 kcal/g
        const proteinCalories = stats.protein * 4; // 蛋白质:4 kcal/g
        const totalMacroCalories = carbCalories + proteinCalories;
        
        // 计算占比
        const carbPercent = totalMacroCalories > 0 ? Math.round((carbCalories / totalMacroCalories) * 100) : 0;
        const proteinPercent = totalMacroCalories > 0 ? Math.round((proteinCalories / totalMacroCalories) * 100) : 0;
        
        // 更新UI
        document.getElementById('carbPercent').textContent = carbPercent + '%';
        document.getElementById('carbGrams').textContent = stats.carb + 'g';
        document.getElementById('carbBar').style.width = carbPercent + '%';
        
        document.getElementById('proteinPercent').textContent = proteinPercent + '%';
        document.getElementById('proteinGrams').textContent = stats.protein + 'g';
        document.getElementById('proteinBar').style.width = proteinPercent + '%';
    },
    
    updateEnergyChart() {
        const stats = DataManager.getTodayStats();
        const container = document.getElementById('energyChart');
        
        const carbEnergy = stats.carb * 4;
        const proteinEnergy = stats.protein * 4;
        
        // 计算柱状图高度
        const maxEnergy = Math.max(carbEnergy, proteinEnergy, 100);
        const carbHeight = (carbEnergy / maxEnergy) * 150;
        const proteinHeight = (proteinEnergy / maxEnergy) * 150;
        
        container.innerHTML = `
            <div class="energy-bar carb" style="height: ${Math.max(carbHeight, 30)}px">
                <span class="energy-value">${carbEnergy}</span>
                <span class="energy-label">碳水能量(kcal)</span>
            </div>
            <div class="energy-bar protein" style="height: ${Math.max(proteinHeight, 30)}px">
                <span class="energy-value">${proteinEnergy}</span>
                <span class="energy-label">蛋白质能量(kcal)</span>
            </div>
        `;
    }
};

代码解析

  1. 热量计算:碳水和蛋白质每克均提供4千卡热量
  2. 占比计算:基于热量贡献计算百分比
  3. 动态渲染:根据计算结果动态更新进度条和柱状图
  4. 最小高度保护:确保柱状图至少显示30px高度

2.2 吸收曲线可视化实现

使用Canvas绘制碳水与蛋白质的吸收曲线对比。

const AbsorbModule = {
    init() {
        this.drawAbsorptionCurve();
    },
    
    drawAbsorptionCurve() {
        const canvas = document.getElementById('absorptionCurve');
        const ctx = canvas.getContext('2d');
        
        // 高清适配
        canvas.width = canvas.offsetWidth * 2;
        canvas.height = canvas.offsetHeight * 2;
        ctx.scale(2, 2);
        
        const width = canvas.offsetWidth;
        const height = canvas.offsetHeight;
        const padding = 40;
        
        // 清空画布
        ctx.clearRect(0, 0, width, height);
        
        // 绘制网格线
        ctx.strokeStyle = '#e2e8f0';
        ctx.lineWidth = 1;
        
        for (let i = 0; i <= 5; i++) {
            const y = padding + (height - 2 * padding) * i / 5;
            ctx.beginPath();
            ctx.moveTo(padding, y);
            ctx.lineTo(width - padding, y);
            ctx.stroke();
        }
        
        // 碳水化合物吸收曲线 (快速吸收)
        ctx.beginPath();
        ctx.strokeStyle = '#f59e0b';
        ctx.lineWidth = 3;
        
        for (let x = 0; x <= 240; x += 5) {
            // 使用指数衰减余弦函数模拟快速吸收曲线
            const y = padding + (height - 2 * padding) * (1 - Math.exp(-x / 40) * Math.cos(x / 30));
            if (x === 0) {
                ctx.moveTo(padding, y);
            } else {
                ctx.lineTo(padding + (width - 2 * padding) * x / 240, y);
            }
        }
        ctx.stroke();
        
        // 蛋白质吸收曲线 (慢速吸收)
        ctx.beginPath();
        ctx.strokeStyle = '#8b5cf6';
        ctx.lineWidth = 3;
        
        for (let x = 0; x <= 240; x += 5) {
            // 使用指数衰减线性增长函数模拟慢速吸收曲线
            const y = padding + (height - 2 * padding) * (1 - Math.exp(-x / 80) * (1 + x / 80));
            if (x === 0) {
                ctx.moveTo(padding, y);
            } else {
                ctx.lineTo(padding + (width - 2 * padding) * x / 240, y);
            }
        }
        ctx.stroke();
        
        // 绘制标签
        ctx.font = '12px sans-serif';
        ctx.fillStyle = '#f59e0b';
        ctx.fillText('碳水化合物', width - 120, padding + 20);
        ctx.fillStyle = '#8b5cf6';
        ctx.fillText('蛋白质', width - 80, padding + 40);
    }
};

代码解析

  1. Canvas高清适配:通过双倍尺寸和scale实现高清显示
  2. 吸收曲线数学模型
    • 碳水化合物:1 - exp(-x/40) * cos(x/30) - 快速上升后波动下降
    • 蛋白质:1 - exp(-x/80) * (1 + x/80) - 缓慢持续上升
  3. 网格绘制:提供参考线便于对比
  4. 颜色编码:橙色代表碳水,紫色代表蛋白质

2.3 摄入计算器实现

根据用户体重和目标计算每日营养需求。

const CalculatorModule = {
    init() {
        this.calculateNutrition();
    },
    
    calculateNutrition() {
        const weight = parseFloat(document.getElementById('calcWeight').value) || 65;
        const goal = document.getElementById('calcGoal').value;
        const activity = document.getElementById('calcActivity').value;
        
        // 基础代谢率 (BMR) - 简化计算公式
        const bmr = weight * 24;  // 每公斤体重约24千卡
        
        // 活动系数映射
        const activityMultipliers = {
            sedentary: 1.2,      // 久坐
            light: 1.375,        // 轻度活动
            moderate: 1.55,      // 中度活动
            active: 1.725        // 高度活动
        };
        
        // 目标系数映射
        const goalMultipliers = {
            maintain: 1.0,        // 维持体重
            lose: 0.8,           // 减重(减少20%)
            gain: 1.2            // 增肌(增加20%)
        };
        
        // 每日总能量消耗 (TDEE)
        const tdee = bmr * activityMultipliers[activity] * goalMultipliers[goal];
        
        // 计算各营养素目标
        const calories = Math.round(tdee);
        const carb = Math.round((calories * 0.5) / 4);      // 碳水占50%
        const protein = Math.round(weight * 1.6);           // 每公斤体重1.6g蛋白质
        const fat = Math.round((calories * 0.25) / 9);      // 脂肪占25%
        
        // 更新显示
        document.getElementById('resultCalories').textContent = calories;
        document.getElementById('resultCarb').textContent = carb + 'g';
        document.getElementById('resultProtein').textContent = protein + 'g';
        document.getElementById('resultFat').textContent = fat + 'g';
    }
};

代码解析

  1. BMR计算:简化公式,每公斤体重约24千卡
  2. TDEE计算:BMR × 活动系数 × 目标系数
  3. 营养素分配
    • 碳水化合物:50%热量
    • 蛋白质:每公斤体重1.6克
    • 脂肪:25%热量

2.4 数据管理模块实现

统一的数据管理接口确保数据持久化和一致性。

const DataManager = {
    getFoods() {
        return JSON.parse(localStorage.getItem('nutrition_foods') || JSON.stringify(foodDatabase));
    },
    
    saveFoods(foods) {
        localStorage.setItem('nutrition_foods', JSON.stringify(foods));
    },
    
    getDailyRecords() {
        return JSON.parse(localStorage.getItem('nutrition_records') || '[]');
    },
    
    saveDailyRecords(records) {
        localStorage.setItem('nutrition_records', JSON.stringify(records));
    },
    
    addRecord(food, amount) {
        const records = this.getDailyRecords();
        const today = new Date().toISOString().split('T')[0];
        
        // 计算实际摄入的营养成分
        const carb = (food.carb / 100) * amount;
        const protein = (food.protein / 100) * amount;
        const fat = (food.fat / 100) * amount;
        const calorie = (food.calorie / 100) * amount;
        
        records.push({
            id: Date.now(),
            foodId: food.id,
            name: food.name,
            icon: food.icon,
            amount,
            carb: Math.round(carb * 10) / 10,
            protein: Math.round(protein * 10) / 10,
            fat: Math.round(fat * 10) / 10,
            calorie: Math.round(calorie),
            date: today
        });
        
        this.saveDailyRecords(records);
        return records;
    },
    
    getTodayStats() {
        const records = this.getDailyRecords();
        const today = new Date().toISOString().split('T')[0];
        const todayRecords = records.filter(r => r.date === today);
        
        return todayRecords.reduce((acc, r) => ({
            calories: acc.calories + r.calorie,
            carb: acc.carb + r.carb,
            protein: acc.protein + r.protein,
            fat: acc.fat + r.fat,
            count: acc.count + 1
        }), { calories: 0, carb: 0, protein: 0, fat: 0, count: 0 });
    }
};

代码解析

  1. 数据存储:使用localStorage实现本地持久化
  2. 默认数据:首次使用时加载预设食物数据库
  3. 营养计算:根据食物营养成分和摄入量计算实际摄入
  4. 统计聚合:使用reduce方法计算今日总摄入

三、食物数据库设计

3.1 食物数据模型

// 食物数据库结构
const foodDatabase = [
    { 
        id: 1, 
        name: '米饭', 
        icon: '🍚', 
        category: 'grain', 
        carb: 25.9, 
        protein: 2.6, 
        fat: 0.3, 
        calorie: 116 
    },
    { 
        id: 8, 
        name: '鸡胸肉', 
        icon: '🍗', 
        category: 'meat', 
        carb: 0.0, 
        protein: 31.0, 
        fat: 1.2, 
        calorie: 133 
    },
    // ... 更多食物数据
];

3.2 食物分类

分类 图标 食物数量 特点
谷物类 🌾 7种 高碳水、低蛋白质
肉类 🥩 7种 高蛋白、低碳水
蔬菜类 🥬 6种 低热量、高纤维
水果类 🍎 5种 中等碳水、维生素丰富
奶制品 🥛 5种 均衡营养

3.3 搜索和筛选功能

searchFoods() {
    const searchValue = document.getElementById('foodSearch').value.toLowerCase();
    const foods = DataManager.getFoods();
    
    let filtered = foods;
    
    // 分类筛选
    if (currentCategory !== 'all') {
        filtered = filtered.filter(f => f.category === currentCategory);
    }
    
    // 关键词搜索
    if (searchValue) {
        filtered = filtered.filter(f => f.name.toLowerCase().includes(searchValue));
    }
    
    // 渲染结果
    this.renderFoods(filtered);
}

四、营养报告模块

4.1 饼图可视化

updateReport() {
    const stats = DataManager.getTodayStats();
    
    // 计算各营养素热量占比
    const total = stats.carb * 4 + stats.protein * 4 + stats.fat * 9;
    const carbDegree = total > 0 ? ((stats.carb * 4) / total) * 360 : 0;
    const proteinDegree = total > 0 ? ((stats.protein * 4) / total) * 360 : 0;
    
    // 使用CSS conic-gradient绘制饼图
    const pieChart = document.getElementById('pieChart');
    pieChart.style.background = `conic-gradient(
        #f59e0b 0deg,
        #f59e0b ${carbDegree}deg,
        #8b5cf6 ${carbDegree}deg,
        #8b5cf6 ${carbDegree + proteinDegree}deg,
        #ef4444 ${carbDegree + proteinDegree}deg,
        #ef4444 360deg
    )`;
    
    // 更新图例
    document.getElementById('legendCarb').textContent = total > 0 ? Math.round((stats.carb * 4 / total) * 100) + '%' : '0%';
    document.getElementById('legendProtein').textContent = total > 0 ? Math.round((stats.protein * 4 / total) * 100) + '%' : '0%';
    document.getElementById('legendFat').textContent = total > 0 ? Math.round((stats.fat * 9 / total) * 100) + '%' : '0%';
}

技术要点

  1. CSS conic-gradient:纯CSS实现饼图,无需Canvas
  2. 动态计算角度:根据营养成分比例计算扇形角度
  3. 图例同步更新:保持图例与饼图数据一致

4.2 记录导出功能

exportReport() {
    const stats = DataManager.getTodayStats();
    const records = DataManager.getDailyRecords();
    const today = new Date().toISOString().split('T')[0];
    const todayRecords = records.filter(r => r.date === today);
    
    let report = `营养报告 - ${today}\n\n`;
    report += `总热量: ${stats.calories} kcal\n`;
    report += `碳水化合物: ${stats.carb}g\n`;
    report += `蛋白质: ${stats.protein}g\n`;
    report += `脂肪: ${stats.fat}g\n\n`;
    report += `摄入记录:\n`;
    
    todayRecords.forEach(r => {
        report += `- ${r.name} (${r.amount}g): ${r.calorie} kcal\n`;
    });
    
    console.log(report);
    UIManager.showToast('报告已生成,请查看控制台');
}

五、UI设计与交互体验

5.1 视觉设计风格

色彩方案

  • 主色调:绿色(#10b981)- 代表健康、自然
  • 碳水色:橙色(#f59e0b)- 代表能量、快速
  • 蛋白质色:紫色(#8b5cf6)- 代表智慧、缓慢
  • 脂肪色:红色(#ef4444)- 代表热量

布局特点

  • 卡片式设计,信息层次清晰
  • 响应式布局,适配不同屏幕尺寸
  • 渐变背景和阴影效果,增强视觉层次感

5.2 营养对比卡片

<div class="comparison-cards">
    <div class="comparison-card carb">
        <div class="card-header">
            <span class="card-icon">🍞</span>
            <h3>碳水化合物</h3>
        </div>
        <div class="card-stats">
            <div class="stat-item">
                <span class="stat-value">50%</span>
                <span class="stat-label">每日占比</span>
            </div>
            <div class="stat-item">
                <span class="stat-value">200g</span>
                <span class="stat-label">摄入克数</span>
            </div>
        </div>
        <div class="comparison-bar">
            <div class="bar-fill carb" style="width: 50%"></div>
        </div>
    </div>
    
    <div class="comparison-card protein">
        <!-- 蛋白质卡片结构类似 -->
    </div>
</div>

5.3 响应式设计

@media (max-width: 768px) {
    .comparison-cards {
        grid-template-columns: 1fr;
    }
    
    .energy-chart {
        flex-direction: column;
        align-items: center;
        height: auto;
    }
    
    .calculator-results {
        grid-template-columns: 1fr;
    }
}

六、技术亮点与创新

6.1 Canvas吸收曲线绘制

使用数学函数模拟营养吸收过程:

吸收曲线数学模型

碳水化合物吸收曲线(快速吸收):
y = 1 - e^(-x/40) * cos(x/30)
特点:快速上升,30分钟达到峰值,随后波动下降

蛋白质吸收曲线(慢速吸收):
y = 1 - e^(-x/80) * (1 + x/80)
特点:缓慢上升,2小时达到峰值,持续时间长

时间轴:0 → 240分钟(4小时)

6.2 实时数据同步

所有模块共享同一数据源,实时更新:

数据流向
┌─────────────────────┐
│    添加食物记录      │
└─────────┬───────────┘
          ▼
┌─────────────────────┐
│   DataManager       │ ← 核心数据管理
└─────────┬───────────┘
          ▼
┌─────────────────────────────────────────────┐
│                                           │
▼                                           ▼
CompareModule                        ReportModule
(营养对比)                          (营养报告)
│                                           │
▼                                           ▼
食物数据库                           摄入计算器

6.3 科学计算模型

// 每日热量需求计算公式
const tdee = bmr * activityMultiplier * goalMultiplier;

// 营养素分配原则
// 碳水化合物:45%-65% → 推荐50%
// 蛋白质:10%-35% → 推荐每公斤体重1.6g
// 脂肪:20%-35% → 推荐25%

// 热量换算
// 碳水化合物:1g = 4 kcal
// 蛋白质:1g = 4 kcal
// 脂肪:1g = 9 kcal

七、总结与展望

7.1 项目成果

功能模块 状态 核心特性
营养对比 碳水vs蛋白质对比、能量柱状图
食物数据库 30种食物、分类筛选、搜索
吸收分析 Canvas曲线绘制、影响因素
摄入计算 BMR/TDEE计算、目标导向
营养报告 饼图展示、记录导出

7.2 未来规划

  1. AI智能推荐:根据用户数据提供个性化饮食建议
  2. 运动追踪:结合运动数据调整营养需求
  3. 云端同步:支持多设备数据同步
  4. 社区分享:分享饮食计划和成果

7.3 技术价值

营养对比分析应用展示了如何使用Web技术栈在鸿蒙PC平台上开发健康类应用,为开发者提供了以下参考:

  • Canvas科学数据可视化技术
  • 数学模型在前端的应用
  • 营养计算算法实现
  • 用户交互体验设计

通过本项目的实践,开发者可以快速掌握健康类应用开发的核心技术,为构建更多优秀应用奠定基础。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐