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

html5 手机端小说模板
(图片来源网络,侵删)

核心设计理念

  1. 沉浸式阅读:默认采用深色模式,减少眼部疲劳,大字体、舒适的行高和字间距,让阅读更轻松。
  2. 高度可定制:用户可以自由调整字体大小、背景颜色、主题等,满足不同人的阅读习惯。
  3. 流畅的翻页:使用 CSS 实现平滑的上下翻页效果,模拟真实翻书感。
  4. 便捷的导航:提供目录、进度跳转、返回顶部等快捷功能。
  5. 响应式设计:完美适配各种手机屏幕尺寸。

最终效果预览


完整代码 (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 事件,根据滚动位置决定是否显示返回顶部按钮,点击后平滑滚动到顶部。
  • 无限滚动: 监听滚动条位置,当用户滚动到页面底部附近时,模拟加载更多内容,这是长篇小说阅读的必备功能。
  • 进度提示: 点击进度按钮,计算并显示当前阅读的百分比。

如何扩展与定制

这个模板是一个功能强大的起点,你可以基于它进行扩展:

  1. 数据源:

    • 静态: 将小说内容直接写在 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;
      }
  2. 本地存储:

    html5 手机端小说模板
    (图片来源网络,侵删)
    • 使用 localStorage 来保存用户的阅读设置(主题、字体大小)和阅读进度。

      // 保存设置
      localStorage.setItem('theme', 'light-theme');
      localStorage.setItem('fontSize', '20px');
      // 读取设置
      const savedTheme = localStorage.getItem('theme');
      if (savedTheme) body.className = savedTheme;
  3. 增加更多功能:

    • 日/夜间模式自动切换: 使用 window.matchMedia('(prefers-color-scheme: dark)') 来检测系统主题。
    • 翻页动画: 使用 CSS 3D 变换或 transform: rotateX() 来模拟更真实的翻书效果。
    • 手势支持: 使用 touchstart, touchmove, touchend 事件实现左右滑动翻页。
    • 朗读功能: 使用 Web Speech API 的 SpeechSynthesis 接口实现 TTS (Text-to-Speech)。
    • 书签功能: 允许用户保存特定章节的阅读位置。

这个模板为你提供了一个坚实、现代且用户友好的基础,希望能帮助你构建出出色的手机端小说阅读应用!

html5 手机端小说模板
(图片来源网络,侵删)