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

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

演示效果

鸿蒙PC Electron框架实战:儿童故事技术实现详解

2

一、项目概述

1.1 项目背景

儿童故事是一款专为3-8岁儿童设计的睡前故事应用。在数字化时代,越来越多的孩子习惯使用电子设备,但市面上很多应用内容良莠不齐,设计也过于成人化。本应用旨在为孩子们提供一个纯净、温馨、有教育意义的故事阅读平台。

应用收录了8个经典儿童故事,涵盖动物故事、童话故事、寓言故事和科普故事四大类别。每个故事都经过精心编排,分段清晰,适合儿童阅读或家长讲述。应用采用梦幻的彩虹色系设计,配合流畅的动画效果,让孩子们在温馨的氛围中进入甜美的梦乡。

1.2 设计理念

理念 说明
温馨安全 纯净内容,无广告干扰
简单易用 界面简洁,适合儿童操作
分段阅读 每页一段,避免视觉疲劳
教育意义 故事蕴含道理,寓教于乐
语音朗读 自动朗读故事,解放家长双手

1.3 功能模块

模块 功能
故事库 浏览、筛选、收藏所有故事
阅读器 分页阅读、翻页导航
我的收藏 快速访问收藏故事
夜间模式 保护眼睛的暗色主题
语音朗读 Web Speech API自动朗读故事

二、技术架构设计

2.1 整体架构

┌─────────────────────────────────────────────────────────────┐
│                    前端应用层                              │
│         (HTML/CSS/JavaScript + 故事阅读系统)              │
├─────────────────────────────────────────────────────────────┤
│              Electron + Preload 层                        │
│              (IPC 通信、API 暴露)                        │
├─────────────────────────────────────────────────────────────┤
│               HarmonyOS 原生层                             │
│         (libadapter.so + ETS Adapters)                   │
└─────────────────────────────────────────────────────────────┘

2.2 模块划分

模块 职责 文件
视图层 HTML结构、页面组件 index.html
样式层 彩虹色系、CSS动画 style.css
业务层 故事逻辑、状态管理 js/app.js
数据层 故事内容定义 js/app.js (内置)

2.3 数据结构设计

故事数据结构:

const story = {
    id: 1,
    title: '小兔子学游泳',
    emoji: '🐰',
    category: 'animal',
    categoryName: '🐾 动物故事',
    age: '3-6岁',
    desc: '小兔子米米害怕水,但在朋友的鼓励下学会了游泳',
    pages: [
        '森林里住着一只小兔子...',
        '可是米米有一个大问题...',
        // 更多段落...
    ]
};

应用状态管理:

let appState = {
    currentView: 'library',      // 当前视图
    currentCategory: 'all',        // 当前分类
    currentStory: null,            // 当前阅读的故事
    currentPage: 0,               // 当前页码
    favorites: new Set(),         // 收藏的故事ID集合
    isPlaying: false,             // 是否正在播放
    nightMode: false,             // 夜间模式
    speechSynth: window.speechSynthesis,  // Web Speech API
    utterance: null,              // 当前朗读对象
    autoNextPage: false           // 自动翻页开关
};

三、核心功能实现详解

3.1 应用初始化流程

应用启动时执行完整的初始化操作:

function initApp() {
    // 1. 从本地存储加载数据
    loadData();
    
    // 2. 渲染故事网格
    renderStoryGrid();
    
    // 3. 渲染收藏列表
    renderFavorites();
    
    // 4. 更新收藏数量
    updateFavCount();
    
    // 5. 设置事件监听
    setupEventListeners();
}

数据加载函数

function loadData() {
    // 加载收藏列表
    const savedFavs = localStorage.getItem('storyFavorites');
    if (savedFavs) {
        appState.favorites = new Set(JSON.parse(savedFavs));
    }
    
    // 加载夜间模式设置
    const savedNightMode = localStorage.getItem('nightMode');
    if (savedNightMode === 'true') {
        appState.nightMode = true;
        document.body.classList.add('night-mode');
    }
}

3.2 故事数据管理

应用内置8个经典儿童故事,每个故事包含多个段落:

const stories = [
    {
        id: 1,
        title: '小兔子学游泳',
        emoji: '🐰',
        category: 'animal',
        categoryName: '🐾 动物故事',
        age: '3-6岁',
        desc: '小兔子米米害怕水,但在朋友的鼓励下学会了游泳',
        pages: [
            '森林里住着一只小兔子,名叫米米。米米有一身雪白的毛,长长的耳朵,红红的眼睛,非常可爱。',
            '可是米米有一个大问题——她特别害怕水。每次洗澡的时候,她都会躲到沙发下面去。',
            // 12个段落,完整故事
        ]
    },
    // 更多故事...
];

故事分段的设计考虑:

  • 每段约100-150字,适合儿童注意力持续时间
  • 段落之间有自然的情节过渡
  • 便于语音朗读时的停顿和翻页

3.3 故事网格渲染

故事库页面的核心渲染函数:

function renderStoryGrid() {
    const grid = document.getElementById('storyGrid');
    let filteredStories = stories;
    
    // 应用分类筛选
    if (appState.currentCategory !== 'all') {
        filteredStories = stories.filter(s => s.category === appState.currentCategory);
    }
    
    // 渲染故事卡片
    grid.innerHTML = filteredStories.map(story => `
        <div class="story-card" data-id="${story.id}">
            <div class="story-card-cover">
                <span class="cover-emoji">${story.emoji}</span>
                <button class="story-card-fav ${appState.favorites.has(story.id) ? 'active' : ''}" data-id="${story.id}">
                    ${appState.favorites.has(story.id) ? '❤️' : '🤍'}
                </button>
            </div>
            <div class="story-card-content">
                <div class="story-card-title">${story.title}</div>
                <div class="story-card-desc">${story.desc}</div>
                <div class="story-card-meta">
                    <span class="story-card-tag">${story.categoryName}</span>
                    <span class="story-card-tag">${story.age}</span>
                </div>
            </div>
        </div>
    `).join('');
    
    // 绑定点击事件
    bindCardEvents();
}

事件绑定函数

function bindCardEvents() {
    const grid = document.getElementById('storyGrid');
    
    // 故事卡片点击 - 打开阅读器
    grid.querySelectorAll('.story-card').forEach(card => {
        card.addEventListener('click', (e) => {
            if (!e.target.classList.contains('story-card-fav')) {
                openStory(parseInt(card.dataset.id));
            }
        });
    });
    
    // 收藏按钮点击
    grid.querySelectorAll('.story-card-fav').forEach(btn => {
        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            toggleFavorite(parseInt(btn.dataset.id));
        });
    });
}

3.4 分类筛选系统

分类切换是儿童类应用的重要功能:

function switchCategory(category) {
    appState.currentCategory = category;
    
    // 更新标签按钮状态
    document.querySelectorAll('.tab-btn').forEach(btn => {
        btn.classList.toggle('active', btn.dataset.category === category);
    });
    
    // 重新渲染网格
    renderStoryGrid();
}

HTML中的分类标签:

<div class="category-tabs" id="categoryTabs">
    <button class="tab-btn active" data-category="all">全部</button>
    <button class="tab-btn" data-category="animal">🐾 动物故事</button>
    <button class="tab-btn" data-category="fairy">🧚 童话故事</button>
    <button class="tab-btn" data-category="fable">📖 寓言故事</button>
    <button class="tab-btn" data-category="science">🔬 科普故事</button>
</div>

3.5 阅读器核心功能

打开故事函数

function openStory(storyId) {
    const story = stories.find(s => s.id === storyId);
    if (!story) return;
    
    // 保存当前状态
    appState.currentStory = story;
    appState.currentPage = 0;
    
    // 更新阅读器UI
    document.getElementById('storyCover').innerHTML = `<span class="cover-emoji">${story.emoji}</span>`;
    document.getElementById('storyTitle').textContent = story.title;
    document.getElementById('storyCategory').textContent = story.categoryName;
    document.getElementById('storyAge').textContent = story.age;
    
    // 更新收藏按钮状态
    const collectBtn = document.getElementById('collectBtn');
    collectBtn.innerHTML = `<span>${appState.favorites.has(story.id) ? '❤️' : '🤍'}</span>`;
    
    // 显示第一页
    showPage(0);
    
    // 切换到阅读视图
    switchView('reader');
    
    // 显示音频控制条
    document.getElementById('audioControls').classList.add('active');
    document.getElementById('audioTitle').textContent = story.title;
}

分页显示函数

function showPage(pageIndex) {
    if (!appState.currentStory) return;
    
    const pages = appState.currentStory.pages;
    if (pageIndex < 0 || pageIndex >= pages.length) return;
    
    // 更新当前页码
    appState.currentPage = pageIndex;
    
    // 渲染故事内容
    const storyBody = document.getElementById('storyBody');
    storyBody.innerHTML = `<p>${pages[pageIndex]}</p>`;
    
    // 更新页码显示
    document.getElementById('currentPage').textContent = pageIndex + 1;
    document.getElementById('totalPages').textContent = pages.length;
    
    // 更新翻页按钮状态
    document.getElementById('prevPage').disabled = pageIndex === 0;
    document.getElementById('nextPage').disabled = pageIndex === pages.length - 1;
}

翻页控制

function prevPage() {
    showPage(appState.currentPage - 1);
}

function nextPage() {
    showPage(appState.currentPage + 1);
}

3.6 收藏系统实现

收藏功能使用Set数据结构存储,Set的特性保证了收藏ID的唯一性:

function toggleFavorite(storyId) {
    if (appState.favorites.has(storyId)) {
        // 取消收藏
        appState.favorites.delete(storyId);
        showToast('已取消收藏');
    } else {
        // 添加收藏
        appState.favorites.add(storyId);
        showToast('已添加到收藏');
    }
    
    // 保存到本地
    saveData();
    
    // 更新UI
    updateFavCount();
    renderStoryGrid();
    renderFavorites();
    
    // 如果正在阅读当前故事,更新收藏按钮
    if (appState.currentStory && appState.currentStory.id === storyId) {
        const collectBtn = document.getElementById('collectBtn');
        collectBtn.innerHTML = `<span>${appState.favorites.has(storyId) ? '❤️' : '🤍'}</span>`;
    }
}

收藏列表渲染

function renderFavorites() {
    const list = document.getElementById('favoritesList');
    
    // 空状态处理
    if (appState.favorites.size === 0) {
        list.innerHTML = `
            <div class="empty-favorites">
                <div class="empty-favorites-icon">📚</div>
                <p>还没有收藏的故事哦</p>
                <p>快去故事库看看吧!</p>
            </div>
        `;
        return;
    }
    
    // 渲染收藏列表
    list.innerHTML = [...appState.favorites].map(favId => {
        const story = stories.find(s => s.id === favId);
        if (!story) return '';
        return `
            <div class="favorite-item" data-id="${story.id}">
                <div class="favorite-icon">${story.emoji}</div>
                <div class="favorite-info">
                    <div class="favorite-title">${story.title}</div>
                    <div class="favorite-meta">${story.categoryName} · ${story.age}</div>
                </div>
                <button class="favorite-remove" data-id="${story.id}">移除</button>
            </div>
        `;
    }).join('');
    
    // 绑定事件
    bindFavoriteEvents();
}

3.7 夜间模式切换

夜间模式通过CSS变量和body类名控制:

function toggleNightMode() {
    appState.nightMode = !appState.nightMode;
    document.body.classList.toggle('night-mode', appState.nightMode);
    saveData();
    showToast(appState.nightMode ? '已开启夜间模式' : '已关闭夜间模式');
}

CSS中的夜间模式样式:

body.night-mode {
    --bg-primary: #1a1a2e;
    --bg-secondary: #252542;
    --bg-warm: #2d2d4a;
    --text-primary: #e0e0f0;
    --text-secondary: #a0a0c0;
    --border: #3a3a5a;
}

body.night-mode .header,
body.night-mode .story-card,
body.night-mode .reader-content,
body.night-mode .favorite-item {
    background: var(--bg-secondary);
}

四、语音朗读功能实现

4.1 Web Speech API介绍

Web Speech API是浏览器原生提供的语音合成接口,无需额外依赖库。主要包含两个核心对象:

  • SpeechSynthesis:语音合成控制器,管理朗读队列
  • SpeechSynthesisUtterance:朗读内容对象,设置文本和参数

4.2 播放控制核心代码

播放/暂停切换函数

function togglePlayPause() {
    // 检查浏览器支持
    if (!appState.speechSynth) {
        showToast('当前浏览器不支持语音朗读');
        return;
    }
    
    if (appState.isPlaying) {
        pauseSpeech();
    } else {
        startSpeech();
    }
}

4.3 开始朗读实现

function startSpeech() {
    if (!appState.currentStory) {
        showToast('请先选择一个故事');
        return;
    }
    
    const pages = appState.currentStory.pages;
    const currentText = pages[appState.currentPage];
    
    // 创建朗读对象
    appState.utterance = new SpeechSynthesisUtterance(currentText);
    
    // 设置中文语音参数
    appState.utterance.lang = 'zh-CN';      // 中文语言
    appState.utterance.rate = 0.9;          // 语速(稍慢,适合儿童)
    appState.utterance.pitch = 1.1;         // 音调(稍高,更活泼)
    appState.utterance.volume = 1;          // 音量(最大)
    
    // 朗读开始事件
    appState.utterance.onstart = () => {
        appState.isPlaying = true;
        document.getElementById('playIcon').textContent = '⏸️';
        appState.autoNextPage = true;
        updatePlaybackProgress();
    };
    
    // 朗读结束事件 - 自动翻页
    appState.utterance.onend = () => {
        if (appState.autoNextPage && appState.currentPage < pages.length - 1) {
            // 还有下一页,继续朗读
            setTimeout(() => {
                nextPage();
                if (appState.isPlaying) {
                    startSpeech();
                }
            }, 500);  // 500ms间隔,自然过渡
        } else {
            // 最后一页,停止播放
            stopPlayback();
        }
    };
    
    // 朗读错误处理
    appState.utterance.onerror = () => {
        stopPlayback();
        showToast('朗读出错');
    };
    
    // 开始朗读
    appState.speechSynth.speak(appState.utterance);
}

参数说明

参数 说明
lang ‘zh-CN’ 中文语音,系统会自动选择中文语音引擎
rate 0.9 语速稍慢,适合儿童理解故事内容
pitch 1.1 音调稍高,听起来更活泼有趣
volume 1 最大音量,确保睡前能清晰听到

4.4 暂停与继续朗读

function pauseSpeech() {
    if (appState.speechSynth) {
        appState.speechSynth.pause();
        appState.isPlaying = false;
        appState.autoNextPage = false;
        document.getElementById('playIcon').textContent = '▶️';
    }
}

注意:Web Speech API的pause()方法在某些浏览器上可能不完全支持,实际使用时可能需要用cancel()然后重新speak()来实现暂停效果。

4.5 停止朗读

function stopPlayback() {
    appState.isPlaying = false;
    appState.autoNextPage = false;
    
    // 取消所有朗读
    if (appState.speechSynth) {
        appState.speechSynth.cancel();
    }
    
    // 清除进度更新定时器
    if (playbackInterval) {
        clearInterval(playbackInterval);
    }
    
    // 重置UI状态
    document.getElementById('playIcon').textContent = '▶️';
    document.getElementById('progressFill').style.width = '0%';
    document.getElementById('currentTime').textContent = '0:00';
}

4.6 进度条更新

let playbackInterval = null;

function updatePlaybackProgress() {
    if (playbackInterval) clearInterval(playbackInterval);
    
    playbackInterval = setInterval(() => {
        if (!appState.isPlaying || !appState.utterance) {
            clearInterval(playbackInterval);
            return;
        }
        
        // 计算进度百分比
        const totalPages = appState.currentStory.pages.length;
        const currentPage = appState.currentPage;
        const percent = ((currentPage + 1) / totalPages) * 100;
        
        // 更新进度条和显示
        document.getElementById('progressFill').style.width = `${percent}%`;
        document.getElementById('currentTime').textContent = `${currentPage + 1}/${totalPages}`;
        document.getElementById('duration').textContent = `${totalPages}/${totalPages}`;
    }, 500);  // 每500ms更新一次
}

4.7 进度条点击跳转

// 进度条点击 - 跳转到指定页
document.getElementById('progressBar').addEventListener('click', (e) => {
    if (!appState.currentStory) return;
    
    const rect = e.target.getBoundingClientRect();
    const percent = (e.clientX - rect.left) / rect.width;
    const totalPages = appState.currentStory.pages.length;
    const targetPage = Math.floor(percent * totalPages);
    
    // 跳转到目标页
    showPage(targetPage);
    
    // 如果正在朗读,停止当前朗读并开始新页朗读
    if (appState.isPlaying) {
        appState.speechSynth.cancel();
        startSpeech();
    }
    
    // 更新进度显示
    document.getElementById('progressFill').style.width = `${percent * 100}%`;
    document.getElementById('currentTime').textContent = `${targetPage + 1}/${totalPages}`;
});

4.8 自动翻页机制

语音朗读完成后自动翻到下一页继续朗读:

appState.utterance.onend = () => {
    if (appState.autoNextPage && appState.currentPage < pages.length - 1) {
        setTimeout(() => {
            nextPage();  // 翻到下一页
            if (appState.isPlaying) {
                startSpeech();  // 继续朗读新页面
            }
        }, 500);
    } else {
        stopPlayback();  // 最后一页,停止
    }
};

4.9 Web Speech API兼容性

浏览器 支持情况 说明
Chrome 33+ 完全支持 需要网络连接加载语音
Firefox 49+ 完全支持 本地语音引擎
Safari 7+ 完全支持 macOS/iOS原生支持
Edge 14+ 完全支持 Windows原生支持
Electron 完全支持 基于Chromium

五、前端样式设计

5.1 彩虹色系主题

:root {
    /* 主色调 - 温暖粉色 */
    --primary: #ff6b9d;
    --primary-light: #ff8fb3;
    --primary-dark: #e55a8a;
    
    /* 彩虹辅助色 */
    --rainbow-1: #ff6b6b;  /* 红 */
    --rainbow-2: #ffa94d;  /* 橙 */
    --rainbow-3: #ffd43b;  /* 黄 */
    --rainbow-4: #69db7c;  /* 绿 */
    --rainbow-5: #74c0fc;  /* 蓝 */
    --rainbow-6: #b197fc;  /* 紫 */
    
    /* 背景色 */
    --bg-primary: #fff5f7;
    --bg-secondary: #ffffff;
    --bg-warm: #fff9f0;
}

5.2 Logo彩虹渐变

.logo h1 {
    font-size: 1.5rem;
    font-weight: 700;
    background: linear-gradient(90deg, 
        var(--rainbow-1), 
        var(--rainbow-2), 
        var(--rainbow-3), 
        var(--rainbow-4), 
        var(--rainbow-5), 
        var(--rainbow-6));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

5.3 Banner浮动云朵动画

.cloud {
    position: absolute;
    font-size: 3rem;
    opacity: 0.3;
    animation: floatCloud 6s ease-in-out infinite;
}

.cloud-1 { top: 20%; left: 10%; animation-delay: 0s; }
.cloud-2 { top: 40%; right: 15%; animation-delay: 2s; }
.cloud-3 { bottom: 20%; left: 30%; animation-delay: 4s; }

@keyframes floatCloud {
    0%, 100% { transform: translateX(0) translateY(0); }
    50% { transform: translateX(20px) translateY(-10px); }
}

5.4 故事卡片悬浮效果

.story-card {
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    transition: all var(--transition-normal);
}

.story-card:hover {
    transform: translateY(-8px);
    box-shadow: var(--shadow-lg);
}

5.5 浮动装饰动画

.floating-decorations {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 0;
    overflow: hidden;
}

.float-item {
    position: absolute;
    left: var(--x);
    top: var(--y);
    font-size: 1.5rem;
    opacity: 0.4;
    animation: floatUp 4s ease-in-out infinite;
    animation-delay: var(--delay);
}

@keyframes floatUp {
    0%, 100% { 
        transform: translateY(0) rotate(0deg); 
        opacity: 0.4;
    }
    50% { 
        transform: translateY(-30px) rotate(180deg); 
        opacity: 0.7;
    }
}

5.6 音频控制条样式

.audio-controls {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--bg-secondary);
    box-shadow: 0 -4px 20px rgba(0,0,0,0.1);
    padding: var(--spacing-md) var(--spacing-lg);
    display: flex;
    align-items: center;
    gap: var(--spacing-lg);
    transform: translateY(100%);
    transition: transform var(--transition-normal);
    z-index: 99;
}

.audio-controls.active {
    transform: translateY(0);
}

.progress-bar {
    height: 6px;
    background: var(--border);
    border-radius: var(--radius-full);
    overflow: hidden;
    cursor: pointer;
}

.progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary), var(--rainbow-5));
    width: 0%;
    transition: width 0.1s linear;
}

5.7 响应式布局

@media (max-width: 768px) {
    .header-content {
        flex-direction: column;
        gap: var(--spacing-md);
    }
    
    .story-grid {
        grid-template-columns: 1fr;
    }
    
    .category-tabs {
        overflow-x: auto;
        flex-wrap: nowrap;
    }
    
    .audio-controls {
        flex-wrap: wrap;
        gap: var(--spacing-md);
    }
}

@media (max-width: 480px) {
    .logo h1 {
        font-size: 1.2rem;
    }
    
    .reader-content {
        padding: var(--spacing-md);
    }
}

六、事件监听系统

6.1 完整事件绑定

function setupEventListeners() {
    // 分类标签点击
    document.querySelectorAll('.tab-btn').forEach(btn => {
        btn.addEventListener('click', () => switchCategory(btn.dataset.category));
    });
    
    // 返回按钮
    document.getElementById('backBtn').addEventListener('click', goBack);
    
    // 收藏按钮
    document.getElementById('collectBtn').addEventListener('click', () => {
        if (appState.currentStory) {
            toggleFavorite(appState.currentStory.id);
        }
    });
    
    // 分页按钮
    document.getElementById('prevPage').addEventListener('click', prevPage);
    document.getElementById('nextPage').addEventListener('click', nextPage);
    
    // 收藏页按钮
    document.getElementById('favoritesBtn').addEventListener('click', () => {
        switchView('favorites');
        renderFavorites();
    });
    
    // 夜间模式按钮
    document.getElementById('nightModeBtn').addEventListener('click', toggleNightMode);
    
    // 音频控制
    document.getElementById('playPauseBtn').addEventListener('click', togglePlayPause);
    document.getElementById('stopBtn').addEventListener('click', stopPlayback);
    
    // 进度条点击跳转
    document.getElementById('progressBar').addEventListener('click', handleProgressClick);
}

七、数据管理

7.1 LocalStorage数据结构

Key 数据类型 说明
storyFavorites Array 收藏的故事ID列表
nightMode String 夜间模式开关

7.2 数据流转

用户点击收藏
    ↓
Set添加/删除ID
    ↓
保存到localStorage
    ↓
更新收藏数量
    ↓
重新渲染UI

7.3 数据保存函数

function saveData() {
    // Set转数组后保存
    localStorage.setItem('storyFavorites', JSON.stringify([...appState.favorites]));
    localStorage.setItem('nightMode', appState.nightMode);
}

八、代码优化建议

8.1 性能优化

使用DocumentFragment减少DOM操作:

function renderStoryGrid() {
    const grid = document.getElementById('storyGrid');
    const fragment = document.createDocumentFragment();
    
    filteredStories.forEach(story => {
        const card = createStoryCard(story);
        fragment.appendChild(card);
    });
    
    grid.innerHTML = '';
    grid.appendChild(fragment);
}

8.2 错误处理

function openStory(storyId) {
    try {
        const story = stories.find(s => s.id === storyId);
        if (!story) {
            console.error('故事不存在:', storyId);
            return;
        }
        // 正常逻辑...
    } catch (error) {
        console.error('打开故事失败:', error);
        showToast('加载失败,请重试');
    }
}

8.3 扩展性设计

添加新故事只需在stories数组中添加对象:

const stories = [
    // 现有故事...
    {
        id: 9,
        title: '新故事名称',
        emoji: '🆕',
        category: 'animal',
        categoryName: '🐾 动物故事',
        age: '3-6岁',
        desc: '故事简介',
        pages: ['第1段内容', '第2段内容', '...']
    }
];

九、总结与展望

9.1 项目成果

儿童故事应用成功实现了以下功能:

  1. 故事库系统:8个经典故事,四大分类筛选
  2. 分页阅读器:清晰的段落分隔,便捷的翻页导航
  3. 收藏系统:Set存储,一键收藏/取消
  4. 夜间模式:CSS变量切换,保护眼睛
  5. 语音朗读:Web Speech API自动朗读故事
  6. 自动翻页:朗读完成后自动继续下一页
  7. 进度控制:进度条显示,点击跳转
  8. 数据持久化:localStorage保存用户数据

9.2 技术亮点

亮点 说明
Web Speech API 浏览器原生语音朗读,无需依赖
Set数据结构 收藏ID去重,操作高效
CSS变量主题 夜间模式切换优雅
事件委托 高效的卡片点击处理
分段阅读 适合儿童注意力特点
动画效果 浮动装饰,云朵飘动

9.3 未来规划

优先级 功能 说明
更多故事 扩充故事库内容
语音选择 允许用户选择不同语音
书签功能 记忆阅读进度
家长控制 设置使用时间
语速调节 用户自定义朗读速度
互动环节 故事问答游戏
音效系统 背景音乐和音效

9.4 技术拓展

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

  • IndexedDB:存储更多故事数据
  • Service Worker:实现离线阅读
  • 动画库:如GSAP实现更复杂动画
  • PWA:支持添加到桌面
  • WebSocket:云端故事同步

附录

核心文件列表

文件 说明
index.html 页面结构,包含故事库、阅读器、收藏页、音频控制条
style.css 彩虹色系样式,包含所有动画和响应式设计
js/app.js 故事数据、应用逻辑和语音朗读功能

故事列表

ID 标题 分类 适龄 页数
1 小兔子学游泳 动物故事 3-6岁 12页
2 星星的愿望 童话故事 4-7岁 12页
3 狼来了和牧童 寓言故事 5-8岁 13页
4 月亮船旅行记 童话故事 3-6岁 12页
5 小蜗牛的大房子 动物故事 4-7岁 12页
6 种子的梦想 科普故事 5-8岁 12页
7 三只小猪盖房子 寓言故事 4-7岁 13页
8 小蝌蚪找妈妈 科普故事 3-6岁 12页

Web Speech API参数表

参数 类型 范围 默认值 说明
lang String 语言代码 ‘zh-CN’ 中文语音
rate Float 0.1-10 0.9 语速
pitch Float 0-2 1.1 音调
volume Float 0-1 1 音量

浏览器兼容性

浏览器 最低版本 Web Speech API
Chrome 33+ 完全支持
Firefox 49+ 完全支持
Safari 7+ 完全支持
Edge 14+ 完全支持

Electron 开发 HarmonyOS PC 项目核心优势

优势分类 具体内容 价值说明
🌐 技术栈零成本 基于 HTML/CSS/JS/TS 开发,无需学习 ArkTS/ArkUI 前端团队直接上手,现有 Web 项目一键迁移到鸿蒙 PC
🖥️ 真正全平台跨端 一套代码同时支持:Windows/macOS/Linux/鸿蒙 PC 一次开发,多端发布,大幅降低开发与维护成本
⚡ 开发效率极高 热重载、DevTools 调试、无需鸿蒙模拟器/真机 开发速度比鸿蒙原生快 2~3 倍,调试体验与 Web 一致
🧩 Node.js 全生态 直接使用 npm 几十万生态库(文件、数据库、网络、加密等) 轻松实现桌面级能力,原生开发难以快速实现
🎨 UI 高度自由 支持 Vue/React 等框架,可自定义无边框/毛玻璃/透明窗口 界面设计不受原生组件限制,视觉效果更灵活
📦 标准鸿蒙打包 可编译为 HAP 格式,上架华为应用市场,通过鸿蒙生态准入 完全融入鸿蒙生态,享受系统级分发能力
🔌 鸿蒙系统能力 支持鸿蒙窗口管理、通知、文件系统、分布式软总线 既保留 Web 灵活性,又具备原生应用的系统能力
🏢 企业级稳定性 成熟稳定、社区庞大、坑少、资料丰富、易招人 适合管理系统、工具软件、客户端等重型企业项目
🚀 快速落地交付 无需适配鸿蒙原生架构,直接迁移现有 PC 客户端 项目上线周期缩短,快速抢占鸿蒙 PC 市场
🛠️ 工程化完善 支持打包、签名、自动更新、多环境配置 满足商用项目的发布、运维、迭代全流程需求
🧪 低门槛测试 本地直接运行,无需真机/虚拟机,降低测试成本 测试效率高,适合快速迭代与需求验证
🔐 生态合规 支持鸿蒙 ACL 权限、签名、Profile 配置 满足上架审核要求,可正式商用

核心技术优势

  1. 低学习门槛,复用前端成熟技术栈
    Electron 基于 HTML、CSS、JavaScript、TypeScript 技术体系开发,开发者无需学习鸿蒙专属的 ArkTS、ArkUI 全新语法与组件体系。前端现有 Vue、React、uniapp 等成熟项目代码可快速迁移、改造适配鸿蒙PC端,极大降低团队技术转型成本和项目开发门槛,适配中小型团队快速落地鸿蒙桌面应用开发。
  2. 跨平台复用性极强,一套代码多端部署
    基于 Electron 开发的项目具备全平台兼容能力,同一套业务代码无需大规模重构,即可同时编译适配 Windows、macOS、Linux、鸿蒙PC 四大桌面平台。彻底解决多端项目重复开发、代码冗余、维护成本高的问题,大幅降低企业多端产品的迭代与运维成本。
  3. 依托 Node.js 生态,桌面能力极强
    Electron 内置完整 Node.js 运行环境,可直接调用海量 NPM 开源生态库,支持文件读写、本地数据库、串口通信、加密解析、日志处理、批量数据处理等复杂桌面级能力。相较于鸿蒙原生开发生态匮乏、部分桌面能力需要原生C/C++适配的问题,Electron 可快速实现重型桌面工具、管理系统、客户端工具的业务需求。
  4. 开发调试效率高,无需依赖鸿蒙虚拟机/真机
    Electron 支持本地快速启动、热重载、Chrome DevTools 可视化调试,开发过程无需依赖鸿蒙模拟器、鸿蒙真机设备。代码修改实时生效,断点调试、性能排查、样式调试完全沿用前端成熟调试体系,开发、自测、问题排查效率远高于原生鸿蒙开发,大幅缩短项目开发周期。
  5. UI 自定义自由度高,视觉表现无限制
    区别于鸿蒙原生固定组件样式与布局限制,Electron 完全支持 Web 全量样式能力,可实现无边框窗口、透明窗口、毛玻璃效果、自定义标题栏、异形布局、动态可视化图表等个性化界面效果。能够满足商业化软件、工具类软件、高颜值桌面客户端的定制化 UI 需求。
  6. 完全兼容鸿蒙生态,满足商用上架规范
    Electron 鸿蒙PC项目为官方合规开发方案,支持标准 HAP 打包、签名配置、ACL 权限声明、鸿蒙生态准入检测。可正常完成证书配置、权限一致性校验、合规检测,最终打包产物可直接上架华为应用市场,完全适配鸿蒙系统安全机制与分发体系,不属于第三方非合规跨端方案。
  7. 原生系统能力兼容,兼顾跨端与系统深度
    Electron for HarmonyOS 并非纯网页应用,可正常调用鸿蒙PC系统级能力,包含系统文件管理、窗口管理、系统通知、剪贴板、全局快捷键、设备信息获取等原生能力。同时支持鸿蒙分布式软总线能力,可实现多设备协同、跨端业务流转,兼顾了 Web 开发的灵活性与原生应用的系统深度。
  8. 工程化体系成熟,适配商用项目迭代
    Electron 具备成熟的打包、加密、更新、多环境配置工程化方案,支持开发、测试、生产多环境区分,支持版本迭代、增量更新、日志监控等商用能力。适配企业级管理系统、桌面工具、行业客户端等长期迭代、持续运维的项目场景。

参考资料

  1. MDN Web Docs - Web Speech API
  2. MDN Web Docs - SpeechSynthesis
  3. MDN Web Docs - SpeechSynthesisUtterance
  4. MDN Web Docs - CSS动画
  5. MDN Web Docs - localStorage
  6. MDN Web Docs - Set数据结构
  7. Electron官方文档
  8. HarmonyOS开发者文档
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐