下面我将为你提供一个完整、可定制、且经过优化的 HTML5 手机端小说模板,这个模板包含了现代小说 App 的核心功能,并提供了详细的代码和注释,方便你二次开发。

(图片来源网络,侵删)
核心设计理念
- 沉浸式阅读:默认采用深色模式,减少眼部疲劳,大字体、舒适的行高和字间距,让阅读更轻松。
- 高度可定制:用户可以自由调整字体大小、背景颜色、主题等,满足不同人的阅读习惯。
- 流畅的翻页:使用 CSS 实现平滑的上下翻页效果,模拟真实翻书感。
- 便捷的导航:提供目录、进度跳转、返回顶部等快捷功能。
- 响应式设计:完美适配各种手机屏幕尺寸。
最终效果预览
完整代码 (index.html)
你可以直接复制以下所有代码,保存为 index.html 文件,然后在浏览器中打开(建议使用手机浏览器或开启手机模拟模式)。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">墨香书阁 - 手机小说阅读</title>
<style>
/* --- 全局样式与变量 --- */
:root {
/* 默认主题变量 - 深色模式 */
--bg-color: #1a1a1a;
--text-color: #e0e0e0;
--font-size: 18px;
--line-height: 1.8;
--paragraph-spacing: 1.2em;
--theme-accent-color: #ff6b6b;
}
/* 浅色主题变量 */
body.light-theme {
--bg-color: #f5f5f5;
--text-color: #333;
--theme-accent-color: #4a90e2;
}
/* 护眼主题变量 */
body.eye-care-theme {
--bg-color: #e8f0e4;
--text-color: #2d4a2b;
--theme-accent-color: #7cb342;
}
/* --- 基础样式重置 --- */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
font-size: var(--font-size);
line-height: var(--line-height);
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* --- 内容容器 --- */
#content-wrapper {
max-width: 100%;
padding: 20px;
transition: background-color 0.3s, color 0.3s;
}
.chapter-title {
text-align: center;
font-size: 1.5em;
font-weight: bold;
margin-bottom: 1.5em;
color: var(--theme-accent-color);
}
.content-text {
text-align: justify;
letter-spacing: 0.05em;
text-indent: 2em;
margin-bottom: var(--paragraph-spacing);
transition: font-size 0.2s, line-height 0.2s;
}
/* --- 底部控制栏 --- */
#bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.9);
color: white;
padding: 10px;
display: flex;
justify-content: space-around;
align-items: center;
z-index: 1000;
transform: translateY(100%);
transition: transform 0.3s ease-in-out;
}
body.show-controls #bottom-bar {
transform: translateY(0);
}
.control-btn {
background: none;
border: none;
color: white;
font-size: 14px;
padding: 5px 10px;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
}
.control-btn i {
font-size: 20px;
}
/* --- 设置面板 --- */
#settings-panel {
position: fixed;
top: 0;
right: -80%; /* 初始在屏幕外 */
width: 80%;
height: 100%;
background-color: rgba(0, 0, 0, 0.95);
color: white;
padding: 20px;
z-index: 2000;
transition: right 0.3s ease-in-out;
overflow-y: auto;
}
body.show-settings #settings-panel {
right: 0;
}
.settings-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
border-bottom: 1px solid #444;
padding-bottom: 10px;
}
.settings-group {
margin-bottom: 25px;
}
.settings-group h3 {
margin-bottom: 10px;
font-size: 1.1em;
}
.theme-options, .font-size-options {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.theme-btn, .font-btn {
padding: 8px 15px;
border: 1px solid #555;
background-color: #333;
color: white;
border-radius: 5px;
cursor: pointer;
transition: all 0.2s;
}
.theme-btn.active, .font-btn.active {
border-color: var(--theme-accent-color);
background-color: var(--theme-accent-color);
}
/* --- 目录面板 --- */
#catalog-panel {
position: fixed;
top: 0;
left: -80%; /* 初始在屏幕外 */
width: 80%;
height: 100%;
background-color: rgba(0, 0, 0, 0.95);
color: white;
padding: 20px;
z-index: 2000;
transition: left 0.3s ease-in-out;
overflow-y: auto;
}
body.show-catalog #catalog-panel {
left: 0;
}
.catalog-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
border-bottom: 1px solid #444;
padding-bottom: 10px;
}
.catalog-list {
list-style: none;
}
.catalog-item {
padding: 12px;
border-bottom: 1px solid #333;
cursor: pointer;
transition: background-color 0.2s;
}
.catalog-item:hover, .catalog-item.active {
background-color: #444;
}
/* --- 返回顶部按钮 --- */
#back-to-top {
position: fixed;
bottom: 70px;
right: 20px;
width: 50px;
height: 50px;
background-color: var(--theme-accent-color);
color: white;
border: none;
border-radius: 50%;
font-size: 20px;
cursor: pointer;
display: none;
align-items: center;
justify-content: center;
z-index: 999;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
#back-to-top.show {
display: flex;
}
/* --- 加载动画 --- */
.loading {
text-align: center;
padding: 20px;
font-size: 1.2em;
color: var(--theme-accent-color);
}
/* --- 滚动条美化 --- */
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-color);
}
::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #777;
}
</style>
</head>
<body>
<!-- 主内容区 -->
<div id="content-wrapper">
<h1 class="chapter-title">第一章 序章:风起青萍</h1>
<div class="content-text">
青云大陆,宗门林立,强者如云,在这片广袤无垠的土地上,流传着无数修仙问道的传说,而故事,便从一个名为“落云村”的小地方开始。
</div>
<div class="content-text">
林凡,一个再普通不过的少年,自幼父母双亡,由村里的猎户王伯抚养长大,他资质平平,在村中同龄人中毫不起眼,唯一的爱好,便是坐在村口的老槐树下,遥望着云雾缭绕的远方,幻想着山外的世界。
</div>
<div class="content-text">
这一天,和往常一样,林凡正在后山砍柴,忽然,天空风云突变,一道惊雷撕裂天际,不偏不倚地落在了他前方不远处的一块青石上,他吓得连忙躲到一棵大树后面,心中惊骇不已。
</div>
<div class="content-text">
待雷声过后,林凡好奇地凑上前去,只见那块青石已经化为齑粉,而在其中心,静静地躺着一枚古朴的黑色戒指,戒指上没有任何纹路,却仿佛蕴含着某种莫大的秘密,散发着淡淡的微光。
</div>
<!-- 更多章节内容... -->
<div class="loading" id="loading-more">加载中...</div>
</div>
<!-- 底部控制栏 -->
<div id="bottom-bar">
<button class="control-btn" id="menu-btn">
<i>☰</i>
<span>目录</span>
</button>
<button class="control-btn" id="settings-btn">
<i>⚙</i>
<span>设置</span>
</button>
<button class="control-btn" id="progress-btn">
<i>📖</i>
<span>进度</span>
</button>
<button class="control-btn" id="theme-btn">
<i>🌙</i>
<span>日/夜</span>
</button>
</div>
<!-- 设置面板 -->
<div id="settings-panel">
<div class="settings-header">
<h2>阅读设置</h2>
<button class="control-btn" id="close-settings">✕</button>
</div>
<div class="settings-group">
<h3>主题选择</h3>
<div class="theme-options">
<button class="theme-btn active" data-theme="default">夜间</button>
<button class="theme-btn" data-theme="light">日间</button>
<button class="theme-btn" data-theme="eye-care">护眼</button>
</div>
</div>
<div class="settings-group">
<h3>字体大小</h3>
<div class="font-size-options">
<button class="font-btn" data-size="16px">小</button>
<button class="font-btn active" data-size="18px">中</button>
<button class="font-btn" data-size="20px">大</button>
<button class="font-btn" data-size="22px">特大</button>
</div>
</div>
</div>
<!-- 目录面板 -->
<div id="catalog-panel">
<div class="catalog-header">
<h2>目录</h2>
<button class="control-btn" id="close-catalog">✕</button>
</div>
<ul class="catalog-list">
<li class="catalog-item active">第一章 序章:风起青萍</li>
<li class="catalog-item">第二章 古戒之谜</li>
<li class="catalog-item">第三章 传承功法</li>
<li class="catalog-item">第四章 初窥门径</li>
<li class="catalog-item">第五章 离开村庄</li>
<li class="catalog-item">...</li>
</ul>
</div>
<!-- 返回顶部按钮 -->
<button id="back-to-top">↑</button>
<script>
// DOM 元素
const body = document.body;
const contentWrapper = document.getElementById('content-wrapper');
const bottomBar = document.getElementById('bottom-bar');
const settingsPanel = document.getElementById('settings-panel');
const catalogPanel = document.getElementById('catalog-panel');
const backToTopBtn = document.getElementById('back-to-top');
const loadingMore = document.getElementById('loading-more');
// 控制按钮
const menuBtn = document.getElementById('menu-btn');
const settingsBtn = document.getElementById('settings-btn');
const closeSettingsBtn = document.getElementById('close-settings');
const closeCatalogBtn = document.getElementById('close-catalog');
const themeBtn = document.getElementById('theme-btn');
const progressBtn = document.getElementById('progress-btn');
// 设置面板元素
const themeButtons = document.querySelectorAll('.theme-btn');
const fontButtons = document.querySelectorAll('.font-btn');
const catalogItems = document.querySelectorAll('.catalog-item');
// --- 核心交互逻辑 ---
// 1. 显示/隐藏控制栏
let hideControlsTimeout;
function showControls() {
body.classList.add('show-controls');
clearTimeout(hideControlsTimeout);
hideControlsTimeout = setTimeout(() => {
body.classList.remove('show-controls');
}, 3000); // 3秒后自动隐藏
}
contentWrapper.addEventListener('click', showControls);
bottomBar.addEventListener('click', (e) => {
e.stopPropagation(); // 防止点击按钮时触发 contentWrapper 的点击事件
});
// 2. 设置面板
settingsBtn.addEventListener('click', () => {
body.classList.add('show-settings');
});
closeSettingsBtn.addEventListener('click', () => {
body.classList.remove('show-settings');
});
// 3. 目录面板
menuBtn.addEventListener('click', () => {
body.classList.add('show-catalog');
});
closeCatalogBtn.addEventListener('click', () => {
body.classList.remove('show-catalog');
});
// 4. 主题切换
themeButtons.forEach(btn => {
btn.addEventListener('click', () => {
themeButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const theme = btn.dataset.theme;
body.className = theme === 'default' ? '' : `${theme}-theme`;
// 更新日/夜按钮图标
const icon = themeBtn.querySelector('i');
if (theme === 'light') {
icon.textContent = '☀';
} else {
icon.textContent = '🌙';
}
});
});
// 5. 字体大小切换
fontButtons.forEach(btn => {
btn.addEventListener('click', () => {
fontButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const size = btn.dataset.size;
document.documentElement.style.setProperty('--font-size', size);
});
});
// 6. 目录项点击
catalogItems.forEach(item => {
item.addEventListener('click', () => {
catalogItems.forEach(i => i.classList.remove('active'));
item.classList.add('active');
// 这里应该是加载新章节内容的逻辑
// loadChapter(item.textContent);
alert(`正在跳转到: ${item.textContent}`);
body.classList.remove('show-catalog');
// 滚动到顶部
window.scrollTo({ top: 0, behavior: 'smooth' });
});
});
// 7. 返回顶部按钮
window.addEventListener('scroll', () => {
if (window.scrollY > 300) {
backToTopBtn.classList.add('show');
} else {
backToTopBtn.classList.remove('show');
}
});
backToTopBtn.addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// 8. 模拟加载更多内容 (无限滚动)
window.addEventListener('scroll', () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
if (loadingMore.style.display !== 'block') {
loadingMore.style.display = 'block';
// 模拟网络请求延迟
setTimeout(() => {
for (let i = 0; i < 5; i++) {
const p = document.createElement('p');
p.className = 'content-text';
p.textContent = '这是新加载的段落内容,用于模拟小说章节的无限加载功能,随着读者不断向下滑动,新的内容会自动加载出来,提供不间断的阅读体验。';
contentWrapper.insertBefore(p, loadingMore);
}
loadingMore.style.display = 'none';
}, 1000);
}
}
});
// 9. 进度按钮 (示例)
progressBtn.addEventListener('click', () => {
const scrollPercent = Math.round((window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100);
alert(`当前阅读进度: ${scrollPercent}%`);
});
// 阻止双击缩放
let lastTouchEnd = 0;
document.addEventListener('touchend', (e) => {
const now = Date.now();
if (now - lastTouchEnd <= 300) {
e.preventDefault();
}
lastTouchEnd = now;
}, false);
</script>
</body>
</html>
代码结构与功能详解
HTML 结构
<body>: 根容器,通过添加不同的类(如light-theme)来切换主题。#content-wrapper: 内容包裹区,所有小说文本都在这里,通过padding保证内容不贴边。#bottom-bar: 底部悬浮控制栏,包含目录、设置、进度、主题切换等核心功能。#settings-panel&#catalog-panel: 从侧边滑出的设置和目录面板,提供更详细的功能入口。#back-to-top: 返回顶部按钮,在用户滚动一定距离后显示。
CSS 样式
- CSS 变量 (
root): 这是实现主题切换的核心,我们将颜色、字体大小等定义为变量,切换主题时只需修改变量的值即可。--bg-color: 背景色--text-color: 文字颜色--font-size: 字体大小--theme-accent-color: 主题色(用于按钮、标题等)
- 主题样式: 通过给
<body>添加不同的类(如light-theme),并重新定义 CSS 变量,实现一键切换。 - 响应式设计:
viewport的设置 (width=device-width, initial-scale=1.0, user-scalable=no) 确保了在移动设备上的正确显示和禁止用户缩放,提供更沉浸的体验。 - 过渡动画:
transition属性为面板滑入/滑出、主题切换等操作添加了平滑的动画效果,提升了用户体验。 - 布局: 使用
Flexbox布局来排列控制栏和面板内的元素,布局灵活且对齐方便。
JavaScript 交互
- 控制栏自动隐藏: 点击内容区域后,控制栏显示,3秒后自动隐藏,这避免了控制栏遮挡阅读内容。
- 面板管理: 通过
body.classList.add('show-settings')和remove来控制设置和目录面板的显示与隐藏。 - 主题与字体切换: 监听按钮点击事件,通过
document.documentElement.style.setProperty('--variable-name', 'value')动态修改 CSS 变量,实现实时预览和切换。 - 返回顶部: 监听
scroll事件,根据滚动位置决定是否显示返回顶部按钮,点击后平滑滚动到顶部。 - 无限滚动: 监听滚动条位置,当用户滚动到页面底部附近时,模拟加载更多内容,这是长篇小说阅读的必备功能。
- 进度提示: 点击进度按钮,计算并显示当前阅读的百分比。
如何扩展与定制
这个模板是一个功能强大的起点,你可以基于它进行扩展:
-
数据源:
- 静态: 将小说内容直接写在 HTML 的
div.content-text中。 - 动态 (推荐): 使用 JavaScript 从服务器 API 获取小说数据,你可以将
loadChapter和无限滚动的逻辑替换为fetch请求。async function loadChapter(chapterId) { const response = await fetch(`/api/chapter/${chapterId}`); const data = await response.json(); contentWrapper.innerHTML = `<h1>${data.title}</h1>` + data.content; }
- 静态: 将小说内容直接写在 HTML 的
-
本地存储:
(图片来源网络,侵删)-
使用
localStorage来保存用户的阅读设置(主题、字体大小)和阅读进度。// 保存设置 localStorage.setItem('theme', 'light-theme'); localStorage.setItem('fontSize', '20px'); // 读取设置 const savedTheme = localStorage.getItem('theme'); if (savedTheme) body.className = savedTheme;
-
-
增加更多功能:
- 日/夜间模式自动切换: 使用
window.matchMedia('(prefers-color-scheme: dark)')来检测系统主题。 - 翻页动画: 使用 CSS 3D 变换或
transform: rotateX()来模拟更真实的翻书效果。 - 手势支持: 使用
touchstart,touchmove,touchend事件实现左右滑动翻页。 - 朗读功能: 使用 Web Speech API 的
SpeechSynthesis接口实现 TTS (Text-to-Speech)。 - 书签功能: 允许用户保存特定章节的阅读位置。
- 日/夜间模式自动切换: 使用
这个模板为你提供了一个坚实、现代且用户友好的基础,希望能帮助你构建出出色的手机端小说阅读应用!

(图片来源网络,侵删)
