欢迎光临
我们一直在努力

鸿蒙PC Electron框架实战:儿童故事UI布局设计

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

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

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

一、项目概述

1.1 项目背景

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

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

1.2 设计理念

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

1.3 功能模块

模块功能
故事库 浏览、筛选、收藏所有故事
阅读器 分页阅读、翻页导航
我的收藏 快速访问收藏故事
夜间模式 保护眼睛的暗色主题
音频控制 模拟播放控制

二、技术架构设计

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 // 夜间模式
};


三、核心功能实现详解

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: [
'森林里住着一只小兔子,名叫米米…',
'可是米米有一个大问题——她特别害怕水…',
// 每段一段,分隔清晰
]
},
// 更多故事…
];

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);
}

3.8 音频播放模拟

虽然应用没有真正的音频文件,但实现了完整的播放控制界面:

function togglePlayPause() {
appState.isPlaying = !appState.isPlaying;
document.getElementById('playIcon').textContent = appState.isPlaying ? '⏸️' : '▶️';

// 模拟播放进度
if (appState.isPlaying) {
simulatePlayback();
}
}

let playbackInterval = null;

function simulatePlayback() {
if (playbackInterval) clearInterval(playbackInterval);

let progress = 0;
const duration = 60; // 模拟60秒

playbackInterval = setInterval(() => {
if (!appState.isPlaying) {
clearInterval(playbackInterval);
return;
}

progress += 0.5;
const percent = (progress / duration) * 100;

document.getElementById('progressFill').style.width = `${percent}%`;
document.getElementById('currentTime').textContent = formatTime(Math.floor(progress));

if (progress >= duration) {
clearInterval(playbackInterval);
stopPlayback();
}
}, 100);
}

function stopPlayback() {
appState.isPlaying = false;
document.getElementById('playIcon').textContent = '▶️';
document.getElementById('progressFill').style.width = '0%';
document.getElementById('currentTime').textContent = '0:00';
if (playbackInterval) clearInterval(playbackInterval);
}

3.9 数据持久化

使用localStorage保存用户数据:

function saveData() {
localStorage.setItem('storyFavorites', JSON.stringify([appState.favorites]));
localStorage.setItem('nightMode', appState.nightMode);
}


四、前端样式设计

4.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;
}

4.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;
}

4.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); }
}

4.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);
}

4.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;
}
}

4.6 响应式布局

@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;
}
}

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

.reader-content {
padding: var(–spacing-md);
}
}


五、事件监听系统

5.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);
}


六、数据管理

6.1 LocalStorage数据结构

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

6.2 数据流转

用户点击收藏

Set添加/删除ID

保存到localStorage

更新收藏数量

重新渲染UI


七、代码优化建议

7.1 性能优化

使用DocumentFragment减少DOM操作:

function renderStoryGrid() {
const grid = document.getElementById('storyGrid');
const fragment = document.createDocumentFragment();

// 在片段中构建DOM
filteredStories.forEach(story => {
const card = createStoryCard(story);
fragment.appendChild(card);
});

// 一次性添加到DOM
grid.innerHTML = '';
grid.appendChild(fragment);
}

7.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('加载失败,请重试');
}
}

7.3 扩展性设计

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

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


八、总结与展望

8.1 项目成果

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

  • 故事库系统:8个经典故事,四大分类筛选
  • 分页阅读器:清晰的段落分隔,便捷的翻页导航
  • 收藏系统:Set存储,一键收藏/取消
  • 夜间模式:CSS变量切换,保护眼睛
  • 音频控制界面:模拟播放进度展示
  • 数据持久化:localStorage保存用户数据
  • 8.2 技术亮点

    • Set数据结构:收藏ID去重,操作高效
    • CSS变量主题:夜间模式切换优雅
    • 事件委托:高效的卡片点击处理
    • 分段阅读:适合儿童注意力特点
    • 动画效果:浮动装饰,云朵飘动

    8.3 未来规划

    优先级功能说明
    语音朗读 接入TTS实现自动朗读
    更多故事 扩充故事库内容
    书签功能 记忆阅读进度
    家长控制 设置使用时间
    互动环节 故事问答游戏
    音效系统 背景音乐和音效

    8.4 技术拓展

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

    • Web Speech API:实现真正的语音朗读
    • IndexedDB:存储更多故事数据
    • Service Worker:实现离线阅读
    • 动画库:如GSAP实现更复杂动画
    • PWA:支持添加到桌面

    附录

    核心文件列表

    文件说明
    index.html 页面结构,包含故事库、阅读器、收藏页
    style.css 彩虹色系样式,包含所有动画
    js/app.js 故事数据和应用逻辑

    故事列表

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

    浏览器兼容性

    浏览器最低版本
    Chrome 80+
    Firefox 75+
    Safari 13+
    Edge 80+

    参考资料

  • MDN Web Docs – CSS动画
  • MDN Web Docs – localStorage
  • MDN Web Docs – Set数据结构
  • Electron官方文档
  • HarmonyOS开发者文档
  • 赞(0)
    未经允许不得转载:171主机测评 » 鸿蒙PC Electron框架实战:儿童故事UI布局设计
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址