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

atomgit仓库地址:https://atomgit.com/feng8403000/caihongtangdemeng

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

一、项目概述

1.1 项目背景

彩虹糖的梦是一款基于鸿蒙PC Electron框架开发的梦幻糖果收集游戏。项目采用现代Web技术栈构建,以其充满童趣的设计理念和精美的视觉效果,为用户带来一个五彩缤纷的糖果世界。玩家可以通过点击收集各种可爱的糖果,解锁完整的糖果图鉴,并记录自己的甜蜜愿望。

这个项目展示了如何利用HTML5、CSS3和JavaScript这些基础的Web技术,结合Electron运行时环境,开发出具有丰富交互体验的桌面应用程序。通过这个项目,我们可以学习到状态管理、数据持久化、动画效果实现等核心技术点。

1.2 项目特点

特点 说明
梦幻风格 彩虹渐变色系、浮动糖果装饰、流畅动画
交互丰富 点击收集、分类筛选、弹窗详情、魔法特效
数据持久化 localStorage保存收集进度和愿望
响应式设计 适配多种屏幕尺寸
纯前端实现 无需后端服务器

1.3 技术栈

  • 前端框架: 原生HTML5 + CSS3 + JavaScript ES6+
  • 图标方案: Emoji表情符号
  • 运行时: Electron (HarmonyOS定制版)
  • 原生层: ArkTS + libadapter.so
  • 数据存储: localStorage
  • 构建工具: Hvigor

二、技术架构设计

2.1 整体架构

┌─────────────────────────────────────────────────────────────┐
│                    前端应用层                              │
│         (HTML/CSS/JavaScript + 糖果收集游戏)              │
├─────────────────────────────────────────────────────────────┤
│              Electron + Preload 层                        │
│              (IPC 通信、API 暴露)                        │
├─────────────────────────────────────────────────────────────┤
│               HarmonyOS 原生层                             │
│         (libadapter.so + ETS Adapters)                    │
└─────────────────────────────────────────────────────────────┘

2.2 模块划分

模块 职责 文件
视图层 HTML结构定义 index.html
样式层 视觉样式和动画 style.css
业务层 应用逻辑和状态管理 js/app.js
数据层 糖果数据定义 js/app.js (内置)

2.3 目录结构

ohos_hap/
├── electron/                    # HAP入口模块
├── web_engine/                  # 核心引擎模块
│   └── src/main/
│       ├── ets/
│       │   ├── adapter/         # 鸿蒙原生适配器
│       │   └── jsbindings/       # JS绑定层
│       └── resources/resfile/resources/app/
│           ├── index.html        # 主页面入口
│           ├── style.css         # 样式文件
│           └── js/
│               └── app.js        # 应用逻辑
├── AppScope/
│   └── resources/base/element/
│       └── string.json           # 应用名称配置
└── docs/                        # 文档目录

三、核心功能实现详解

3.1 糖果数据结构设计

糖果数据采用集中式管理,所有糖果信息定义在一个数组中:

const candyData = [
    { 
        id: 1, 
        name: '彩虹糖', 
        emoji: '🍬', 
        type: 'colored', 
        description: '五彩缤纷的彩虹糖,每一颗都有不同的味道和颜色,象征着生活的多彩与美好。' 
    },
    { 
        id: 2, 
        name: '棒棒糖', 
        emoji: '🍭', 
        type: 'colored', 
        description: '甜蜜的棒棒糖,旋转的色彩像彩虹一样美丽,是童年最美好的回忆。' 
    },
    // ... 更多糖果数据
];

数据结构字段说明:

字段 类型 说明
id number 唯一标识符
name string 糖果名称
emoji string 表情符号作为图标
type string 分类:colored/chocolate/cake
description string 详细描述

分类类型映射:

const typeLabels = {
    colored: '彩色糖',
    chocolate: '巧克力',
    cake: '糕点'
};

3.2 状态管理系统

应用采用简单的全局变量管理状态:

// 已收集的糖果ID集合
let collectedCandies = new Set();

// 用户愿望列表
let wishes = [];

状态初始化流程:

function initApp() {
    // 1. 从本地存储加载已收集的糖果
    loadCollectedCandies();
    
    // 2. 从本地存储加载愿望列表
    loadWishes();
    
    // 3. 渲染糖果网格
    renderCandyGrid();
    
    // 4. 渲染图鉴
    renderAlbum();
    
    // 5. 更新统计数据
    updateStats();
    
    // 6. 设置事件监听
    setupEventListeners();
}

数据持久化实现:

function loadCollectedCandies() {
    const saved = localStorage.getItem('collectedCandies');
    if (saved) {
        // 从JSON字符串恢复为Set对象
        collectedCandies = new Set(JSON.parse(saved));
    }
}

function saveCollectedCandies() {
    // Set对象转换为数组后序列化为JSON
    localStorage.setItem('collectedCandies', JSON.stringify([...collectedCandies]));
}

function loadWishes() {
    const saved = localStorage.getItem('candyWishes');
    if (saved) {
        wishes = JSON.parse(saved);
        renderWishes();
    }
}

function saveWishes() {
    localStorage.setItem('candyWishes', JSON.stringify(wishes));
}

3.3 糖果网格渲染

糖果展示区使用动态渲染方式,根据收集状态显示不同的样式:

function renderCandyGrid() {
    const grid = document.getElementById('candyGrid');
    grid.innerHTML = '';  // 清空现有内容
    
    candyData.forEach(candy => {
        // 判断是否已收集
        const isCollected = collectedCandies.has(candy.id);
        
        // 创建卡片元素
        const card = document.createElement('div');
        card.className = `candy-card ${isCollected ? 'collected' : 'not-collected'}`;
        card.innerHTML = `
            <div class="candy-emoji">${candy.emoji}</div>
            <div class="candy-name">${candy.name}</div>
            <div class="candy-type">${getTypeLabel(candy.type)}</div>
        `;
        
        // 绑定点击事件
        card.addEventListener('click', () => handleCandyClick(candy));
        grid.appendChild(card);
    });
}

CSS样式控制:

.candy-card.collected {
    border-color: var(--rainbow-green);
    background: linear-gradient(135deg, #e8f5e9, #ffffff);
}

.candy-card.not-collected {
    opacity: 0.7;
    filter: grayscale(30%);
}

.candy-card.not-collected:hover {
    opacity: 1;
    filter: grayscale(0%);
}

3.4 点击收集机制

当用户点击糖果卡片时的处理流程:

function handleCandyClick(candy) {
    // 检查是否已收集
    const isCollected = collectedCandies.has(candy.id);
    
    // 如果未收集,则执行收集逻辑
    if (!isCollected) {
        collectedCandies.add(candy.id);
        saveCollectedCandies();
        renderCandyGrid();
        renderAlbum();
        updateStats();
        showCollectEffect(candy);
    }
    
    // 显示详情弹窗
    showCandyModal(candy);
}

收集动画效果:

function showCollectEffect(candy) {
    const effect = document.createElement('div');
    effect.className = 'collect-effect';
    effect.innerHTML = `${candy.emoji} +1`;
    effect.style.cssText = `
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        font-size: 3rem;
        animation: collectFloat 1s ease-out forwards;
        z-index: 1000;
        pointer-events: none;
    `;
    document.body.appendChild(effect);
    
    // 1秒后移除动画元素
    setTimeout(() => {
        effect.remove();
    }, 1000);
}

3.5 弹窗详情展示

使用模态框展示糖果详细信息:

function showCandyModal(candy) {
    const modal = document.getElementById('candyModal');
    const body = document.getElementById('modalBody');
    const isCollected = collectedCandies.has(candy.id);
    
    body.innerHTML = `
        <div class="modal-emoji">${candy.emoji}</div>
        <div class="modal-name">${candy.name}</div>
        <div class="modal-type">${getTypeLabel(candy.type)}</div>
        <p class="modal-description">${candy.description}</p>
        <div style="margin-top: 16px;">
            <span style="color: ${isCollected ? '#27ae60' : '#e74c3c'};">
                ${isCollected ? '✓ 已收集' : '尚未收集'}
            </span>
        </div>
    `;
    
    modal.classList.add('active');
}

弹窗动画样式:

.modal-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.5);
    justify-content: center;
    align-items: center;
    z-index: 100;
    backdrop-filter: blur(5px);
}

.modal-overlay.active {
    display: flex;
    animation: fadeIn 0.3s ease;
}

.modal-content {
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    max-width: 400px;
    width: 90%;
    animation: slideUp 0.3s ease;
}

3.6 统计进度系统

实时更新收集进度统计:

function updateStats() {
    const collected = collectedCandies.size;
    const total = candyData.length;
    const percentage = Math.round((collected / total) * 100);
    
    // 更新数值显示
    document.getElementById('collectedCount').textContent = collected;
    document.getElementById('totalCount').textContent = total;
    document.getElementById('percentage').textContent = percentage + '%';
    
    // 更新进度条宽度
    document.getElementById('progressFill').style.width = percentage + '%';
}

3.7 图鉴分类筛选

支持按分类筛选图鉴:

function filterAlbum(tab) {
    const items = document.querySelectorAll('.album-item');
    
    items.forEach(item => {
        const candyId = parseInt(item.dataset.id);
        const candy = candyData.find(c => c.id === candyId);
        
        if (!candy) return;
        
        // 根据筛选条件显示/隐藏
        if (tab === 'all' || candy.type === tab) {
            item.style.display = 'block';
        } else {
            item.style.display = 'none';
        }
    });
    
    // 更新标签按钮状态
    document.querySelectorAll('.tab-btn').forEach(btn => {
        btn.classList.toggle('active', btn.dataset.tab === tab);
    });
}

3.8 愿望系统实现

用户可以提交和查看愿望:

function submitWish() {
    const input = document.getElementById('wishInput');
    const wish = input.value.trim();
    
    if (!wish) return;
    
    // 添加新愿望
    wishes.push({
        id: Date.now(),
        text: wish,
        time: new Date().toLocaleString('zh-CN')
    });
    
    saveWishes();
    renderWishes();
    input.value = '';
}

function renderWishes() {
    const list = document.getElementById('wishList');
    list.innerHTML = '';
    
    // 倒序显示最新愿望
    wishes.slice().reverse().forEach(wish => {
        const item = document.createElement('div');
        item.className = 'wish-item';
        item.innerHTML = `
            <p style="font-weight: 600;">${wish.text}</p>
            <p style="font-size: 0.8rem; opacity: 0.8;">${wish.time}</p>
        `;
        list.appendChild(item);
    });
}

3.9 魔法特效系统

点击按钮触发粒子爆炸动画:

function triggerMagic() {
    const effect = document.getElementById('magicEffect');
    effect.innerHTML = '';
    
    const emojis = ['✨', '🌈', '🍬', '🍭', '🌟', '💫', '⭐', '💖', '🎀', '🎊'];
    
    // 创建20个粒子
    for (let i = 0; i < 20; i++) {
        const particle = document.createElement('span');
        particle.className = 'magic-particle';
        particle.textContent = emojis[Math.floor(Math.random() * emojis.length)];
        
        // 计算粒子运动轨迹
        const angle = (i / 20) * Math.PI * 2;
        const distance = 100 + Math.random() * 100;
        const tx = Math.cos(angle) * distance;
        const ty = Math.sin(angle) * distance;
        
        // 设置CSS自定义属性
        particle.style.setProperty('--tx', tx + 'px');
        particle.style.setProperty('--ty', ty + 'px');
        particle.style.animationDelay = Math.random() * 0.3 + 's';
        
        effect.appendChild(particle);
    }
    
    effect.style.display = 'block';
    
    // 1秒后清除
    setTimeout(() => {
        effect.style.display = 'none';
        effect.innerHTML = '';
    }, 1000);
}

四、前端样式设计

4.1 彩虹色系设计

:root {
    /* 彩虹色系 */
    --rainbow-red: #ff6b6b;
    --rainbow-orange: #ffa94d;
    --rainbow-yellow: #ffd43b;
    --rainbow-green: #69db7c;
    --rainbow-blue: #74c0fc;
    --rainbow-purple: #b197fc;
    --rainbow-pink: #f783ac;
    
    /* 背景色 */
    --bg-primary: #fff5f5;
    --bg-secondary: #ffffff;
    --bg-gradient-start: #fff0f3;
    --bg-gradient-end: #f0f9ff;
}

4.2 渐变文字效果

标题使用彩虹渐变效果:

.logo h1 {
    font-size: 3rem;
    background: linear-gradient(90deg, 
        var(--rainbow-red), 
        var(--rainbow-orange), 
        var(--rainbow-yellow), 
        var(--rainbow-green), 
        var(--rainbow-blue), 
        var(--rainbow-purple)
    );
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
    font-weight: 800;
    letter-spacing: 4px;
}

4.3 动画关键帧

/* 浮动动画 */
@keyframes float {
    0%, 100% { transform: translateY(0) rotate(0deg); }
    50% { transform: translateY(-20px) rotate(10deg); }
}

/* 弹入动画 */
@keyframes bounceIn {
    0% { transform: scale(0); opacity: 0; }
    50% { transform: scale(1.1); }
    100% { transform: scale(1); opacity: 1; }
}

/* 脉冲动画 */
@keyframes pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.05); }
}

/* 粒子爆炸动画 */
@keyframes explode {
    0% { transform: translate(0, 0) scale(1); opacity: 1; }
    100% { transform: translate(var(--tx), var(--ty)) scale(0); opacity: 0; }
}

4.4 响应式布局

@media (max-width: 768px) {
    .stats-container {
        flex-direction: column;
        align-items: center;
    }
    
    .candy-grid {
        grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    }
}

@media (max-width: 480px) {
    .logo h1 {
        font-size: 1.5rem;
    }
}

五、事件监听系统

function setupEventListeners() {
    // 关闭弹窗按钮
    document.getElementById('closeModal').addEventListener('click', closeModal);
    
    // 分类标签切换
    document.querySelectorAll('.tab-btn').forEach(btn => {
        btn.addEventListener('click', () => filterAlbum(btn.dataset.tab));
    });
    
    // 提交愿望按钮
    document.getElementById('submitWish').addEventListener('click', submitWish);
    
    // 愿望输入回车提交
    document.getElementById('wishInput').addEventListener('keypress', (e) => {
        if (e.key === 'Enter') submitWish();
    });
    
    // 魔法按钮
    document.getElementById('magicBtn').addEventListener('click', triggerMagic);
    
    // 点击遮罩关闭弹窗
    document.getElementById('candyModal').addEventListener('click', (e) => {
        if (e.target === document.getElementById('candyModal')) {
            closeModal();
        }
    });
}

六、数据管理

6.1 糖果数据完整列表

ID 名称 Emoji 分类
1 彩虹糖 🍬 彩色糖
2 棒棒糖 🍭 彩色糖
3 棉花糖 🍡 彩色糖
4 水果糖 🍇 彩色糖
5 星星糖 彩色糖
6 巧克力 🍫 巧克力
7 黑巧克力 🍫 巧克力
8 牛奶巧克力 🥛 巧克力
9 曲奇饼干 🍪 糕点
10 纸杯蛋糕 🧁 糕点
11 甜甜圈 🍩 糕点
12 生日蛋糕 🎂 糕点
13 奶油蛋糕 🍰 糕点
14 爆米花 🍿 彩色糖
15 口香糖 🫘 彩色糖
16 果冻 🍮 彩色糖
17 布丁 🍮 糕点
18 马卡龙 🥠 糕点
19 冰淇淋 🍦 彩色糖
20 雪糕 🍧 彩色糖

七、代码优化建议

7.1 潜在改进点

改进项 现状 建议
数据结构 内置JSON 可引入IndexedDB
状态管理 全局变量 可使用观察者模式
渲染性能 直接innerHTML 可使用虚拟DOM
错误处理 基本缺失 增加异常捕获
单元测试 添加Jest测试

7.2 性能优化示例

使用DocumentFragment减少DOM操作:

function renderCandyGrid() {
    const grid = document.getElementById('candyGrid');
    const fragment = document.createDocumentFragment();
    
    candyData.forEach(candy => {
        const card = document.createElement('div');
        card.className = `candy-card ${collectedCandies.has(candy.id) ? 'collected' : 'not-collected'}`;
        card.innerHTML = `
            <div class="candy-emoji">${candy.emoji}</div>
            <div class="candy-name">${candy.name}</div>
            <div class="candy-type">${getTypeLabel(candy.type)}</div>
        `;
        card.addEventListener('click', () => handleCandyClick(candy));
        fragment.appendChild(card);
    });
    
    grid.innerHTML = '';
    grid.appendChild(fragment);
}

八、总结与展望

8.1 项目成果

彩虹糖的梦项目成功实现了以下功能:

  1. 糖果收集系统:点击收集20种不同的糖果,带有精美的动画效果
  2. 进度统计:实时显示收集数量和百分比,进度条可视化
  3. 图鉴系统:支持分类筛选,区分已收集/未收集状态
  4. 愿望功能:用户可提交愿望,数据本地持久化
  5. 魔法特效:粒子爆炸动画,增强互动体验

8.2 技术收获

通过这个项目,我们掌握了以下技术要点:

  • 状态管理:使用Set和数组管理应用状态
  • 数据持久化:localStorage的读写操作
  • DOM操作:动态创建元素、事件委托
  • 动画实现:CSS关键帧、JavaScript动态样式
  • 响应式设计:媒体查询、弹性布局
  • 用户体验:收集反馈、弹窗交互

8.3 未来规划

优先级 功能 说明
音效系统 添加背景音乐和音效
动画升级 引入Lottie动画
社交分享 分享收集成果
成就系统 解锁特殊成就
主题商店 切换不同主题

8.4 技术拓展

未来可以考虑引入以下技术提升:

  • Canvas/WebGL:实现更炫酷的粒子效果
  • Service Worker:实现离线功能和推送通知
  • Web Components:组件化开发
  • PWA:支持添加到桌面
  • TypeScript:类型安全提升代码质量

附录

核心文件列表

文件 行数 说明
index.html ~150 主页面结构
style.css ~710 样式和动画
js/app.js ~280 核心逻辑
string.json ~11 应用配置

浏览器兼容性

  • Chrome 80+
  • Firefox 75+
  • Safari 13+
  • Edge 80+

参考资料

  1. MDN Web Docs - CSS动画
  2. MDN Web Docs - localStorage
  3. Electron官方文档
  4. HarmonyOS开发者文档
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐