// ==================== 章节数据管理 ==================== // 自动从 data/chapter-*.json 文件加载章节数据 let chaptersData = []; let isDataLoaded = false; // 版本号,每次更新时修改 const CACHE_VERSION = '20260328-1050'; // 加载所有章节数据 async function loadChaptersData() { if (isDataLoaded) return chaptersData; const chapters = []; let chapterNum = 1; // 循环加载章节,直到找不到文件 while (true) { try { // 格式化章节号(带前导零) const chapterId = chapterNum.toString().padStart(2, '0'); const response = await fetch(`data/chapter-${chapterId}.json?v=${CACHE_VERSION}`); if (!response.ok) { // 尝试不带前导零的格式 const response2 = await fetch(`data/chapter-${chapterNum}.json?v=${CACHE_VERSION}`); if (!response2.ok) break; const data = await response2.json(); chapters.push(normalizeChapterData(data, chapterNum)); } else { const data = await response.json(); chapters.push(normalizeChapterData(data, chapterNum)); } chapterNum++; } catch (error) { // 没有更多章节了 break; } } // 额外检查小数章节(如107.5) for (let i = 1; i <= chapterNum + 10; i++) { try { const decimalId = i + 0.5; const response = await fetch(`data/chapter-${decimalId}.json?v=${CACHE_VERSION}`); if (response.ok) { const data = await response.json(); chapters.push(normalizeChapterData(data, decimalId)); } } catch (error) { // 忽略错误 } } // 按id排序 chapters.sort((a, b) => parseFloat(a.id) - parseFloat(b.id)); chaptersData = chapters; isDataLoaded = true; // 将数据挂载到 window 对象,供其他函数使用 window.chaptersData = chaptersData; return chaptersData; } // 标准化章节数据 function normalizeChapterData(data, defaultId) { // 从内容中提取描述(前100个字符) const extractDesc = (content) => { if (!content) return ''; // 去除换行和多余空格,取前100字符 const cleanContent = content.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim(); return cleanContent.substring(0, 100) + (cleanContent.length > 100 ? '...' : ''); }; return { id: data.id || defaultId, title: data.title || `第${defaultId}章`, subtitle: data.subtitle || '', desc: data.desc || extractDesc(data.content), content: data.content || '', status: data.status || '已完结', date: data.date || '2026-03-26' }; } // ==================== DOM加载完成后初始化 ==================== document.addEventListener('DOMContentLoaded', async function() { // 先加载章节数据 await loadChaptersData(); initTheme(); initNavigation(); initParticles(); // 根据页面类型初始化不同功能 const currentPage = window.location.pathname.split('/').pop() || 'index.html'; if (currentPage === 'index.html' || currentPage === '') { renderLatestChapters(); } else if (currentPage === 'chapters.html') { renderChaptersList(); setupFilters(); setupSearch(); updateReadingProgress(); } // reader.html 由页面内联脚本处理,不在此处初始化 // 避免冲突导致内容闪烁 // 初始化回到顶部按钮 initScrollTop(); // 初始化阅读器设置 initReaderSettings(); }); // ==================== 主题切换 ==================== function initTheme() { const themeToggle = document.getElementById('themeToggle'); if (!themeToggle) return; // 检查本地存储的主题 const savedTheme = localStorage.getItem('theme') || 'dark'; document.documentElement.setAttribute('data-theme', savedTheme); updateThemeIcon(savedTheme); themeToggle.addEventListener('click', () => { const currentTheme = document.documentElement.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; document.documentElement.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); updateThemeIcon(newTheme); }); } function updateThemeIcon(theme) { const themeToggle = document.getElementById('themeToggle'); if (!themeToggle) return; const icon = themeToggle.querySelector('.theme-icon'); if (icon) { icon.textContent = theme === 'dark' ? '🌙' : '☀️'; } } // ==================== 导航栏 ==================== function initNavigation() { const menuToggle = document.getElementById('menuToggle'); const navLinks = document.querySelector('.nav-links'); if (menuToggle && navLinks) { menuToggle.addEventListener('click', () => { navLinks.classList.toggle('active'); menuToggle.classList.toggle('active'); }); } // 导航栏滚动效果 let lastScroll = 0; const navbar = document.querySelector('.navbar'); if (navbar) { window.addEventListener('scroll', () => { const currentScroll = window.pageYOffset; if (currentScroll > 100) { navbar.style.background = 'rgba(15, 15, 26, 0.95)'; } else { navbar.style.background = 'rgba(15, 15, 26, 0.8)'; } lastScroll = currentScroll; }); } } // ==================== 粒子效果 ==================== function initParticles() { const particlesContainer = document.getElementById('particles'); if (!particlesContainer) return; // 创建粒子 for (let i = 0; i < 30; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.cssText = ` position: absolute; width: ${Math.random() * 4 + 2}px; height: ${Math.random() * 4 + 2}px; background: rgba(99, 102, 241, ${Math.random() * 0.3 + 0.1}); border-radius: 50%; left: ${Math.random() * 100}%; top: ${Math.random() * 100}%; animation: particleFloat ${Math.random() * 10 + 10}s ease-in-out infinite; animation-delay: ${Math.random() * 5}s; `; particlesContainer.appendChild(particle); } } // ==================== 首页最新章节 ==================== function renderLatestChapters() { const container = document.getElementById('latestChapters'); if (!container) return; // 获取最新的6章 const latestChapters = chaptersData.slice(-6).reverse(); container.innerHTML = latestChapters.map(chapter => `
${chapter.id}

${chapter.title}

${chapter.desc}

${chapter.date} ${chapter.status}
`).join(''); } // ==================== 章节列表页 ==================== function renderChaptersList() { const container = document.getElementById('chaptersList'); if (!container) return; // 使用加载的章节数据 const data = window.chaptersData || chaptersData; container.innerHTML = data.map(chapter => `

第${chapter.id}章 ${chapter.title}

${chapter.date}

${chapter.subtitle}

${chapter.desc}

`).join(''); } function setupFilters() { const filterTabs = document.querySelectorAll('.filter-tab'); filterTabs.forEach(tab => { tab.addEventListener('click', () => { filterTabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); const filter = tab.dataset.filter; filterChapters(filter); }); }); } function filterChapters(filter) { const items = document.querySelectorAll('.timeline-item'); const data = window.chaptersData || chaptersData; items.forEach((item, index) => { let show = true; if (filter === 'latest') { show = index >= data.length - 5; } else if (filter === 'unread') { const chapterId = parseInt(item.dataset.chapter); const readChapters = JSON.parse(localStorage.getItem('readChapters') || '[]'); show = !readChapters.includes(chapterId); } if (show) { item.classList.remove('hidden'); item.style.display = ''; } else { item.classList.add('hidden'); item.style.display = 'none'; } }); } function setupSearch() { const searchInput = document.getElementById('searchInput'); if (!searchInput) return; searchInput.addEventListener('input', (e) => { const query = e.target.value.toLowerCase(); const items = document.querySelectorAll('.timeline-item'); items.forEach(item => { const title = item.querySelector('h3').textContent.toLowerCase(); const desc = item.querySelector('.timeline-desc').textContent.toLowerCase(); const match = title.includes(query) || desc.includes(query); if (match) { item.classList.remove('hidden'); item.style.display = ''; } else { item.classList.add('hidden'); item.style.display = 'none'; } }); }); } function updateReadingProgress() { const readChapters = JSON.parse(localStorage.getItem('readChapters') || '[]'); const data = window.chaptersData || chaptersData; const progress = Math.round((readChapters.length / data.length) * 100); const progressFill = document.getElementById('progressFill'); const progressText = document.getElementById('progressText'); if (progressFill) progressFill.style.width = `${progress}%`; if (progressText) progressText.textContent = `阅读进度 ${progress}%`; } // ==================== 阅读器 ==================== async function initReader() { // 确保数据已加载 if (!isDataLoaded) { await loadChaptersData(); } // 获取章节参数(支持小数章节) const urlParams = new URLSearchParams(window.location.search); const chapterId = parseFloat(urlParams.get('id')) || 1; // 加载章节内容 await loadChapter(chapterId); // 初始化阅读器设置 initReaderSettings(); // 初始化导航 initReaderNav(chapterId); // 初始化侧边栏 initSidebar(); // 记录阅读进度 recordReadingProgress(chapterId); // 自动隐藏头部和底部 initAutoHide(); } async function loadChapter(chapterId) { // 支持小数章节,使用parseFloat比较 const chapter = chaptersData.find(c => parseFloat(c.id) === parseFloat(chapterId)); if (!chapter) { // 尝试从JSON文件直接加载 try { const chapterIdStr = chapterId.toString(); const response = await fetch(`data/chapter-${chapterIdStr}.json?v=${CACHE_VERSION}`); if (response.ok) { const data = await response.json(); const normalizedData = normalizeChapterData(data, chapterId); renderChapterContent(normalizedData); return; } } catch (error) { console.error('加载章节失败:', error); } return; } renderChapterContent(chapter); } function renderChapterContent(chapter) { // 更新标题 const chapterTitleEl = document.getElementById('chapterTitle'); if (chapterTitleEl) { chapterTitleEl.textContent = chapter.title; } document.title = `${chapter.title} - 阿拉德:剑之回响`; // 加载内容 const contentEl = document.getElementById('chapterContent'); if (contentEl) { // 将内容中的换行转换为段落 const paragraphs = chapter.content.split('\n\n').filter(p => p.trim()); const contentHtml = paragraphs.map(p => { // 处理分隔线 if (p.trim() === '---') { return '
'; } // 处理普通段落 return `

${p.replace(/\n/g, '
')}

`; }).join(''); contentEl.innerHTML = `

${chapter.title}

${chapter.subtitle}

${contentHtml}
`; } } function initReaderNav(currentId) { const prevBtn = document.getElementById('prevChapter'); const nextBtn = document.getElementById('nextChapter'); const data = window.chaptersData || chaptersData; // 找到当前章节在数组中的索引 const currentIndex = data.findIndex(ch => parseFloat(ch.id) === parseFloat(currentId)); if (prevBtn) { if (currentIndex > 0) { const prevChapter = data[currentIndex - 1]; prevBtn.disabled = false; prevBtn.onclick = () => { window.location.href = `reader.html?id=${prevChapter.id}`; }; } else { prevBtn.disabled = true; } } if (nextBtn) { if (currentIndex < data.length - 1) { const nextChapter = data[currentIndex + 1]; nextBtn.disabled = false; nextBtn.onclick = () => { window.location.href = `reader.html?id=${nextChapter.id}`; }; } else { nextBtn.disabled = true; } } } function initSidebar() { const openSidebar = document.getElementById('openSidebar'); const sidebarClose = document.getElementById('sidebarClose'); const readerSidebar = document.getElementById('readerSidebar'); const overlay = document.getElementById('overlay'); // 生成侧边栏章节列表 const sidebarChapters = document.getElementById('sidebarChapters'); if (sidebarChapters) { const data = window.chaptersData || chaptersData; const currentId = getCurrentChapterId(); sidebarChapters.innerHTML = data.map(chapter => ` ${chapter.id} ${chapter.title} `).join(''); } if (openSidebar && readerSidebar && overlay) { openSidebar.addEventListener('click', () => { readerSidebar.classList.add('active'); overlay.classList.add('active'); }); } if (sidebarClose && readerSidebar && overlay) { sidebarClose.addEventListener('click', () => { readerSidebar.classList.remove('active'); overlay.classList.remove('active'); }); } if (overlay && readerSidebar) { overlay.addEventListener('click', () => { readerSidebar.classList.remove('active'); overlay.classList.remove('active'); }); } } function getCurrentChapterId() { const urlParams = new URLSearchParams(window.location.search); const id = urlParams.get('id'); return id ? parseFloat(id) : 1; } function recordReadingProgress(chapterId) { let readChapters = JSON.parse(localStorage.getItem('readChapters') || '[]'); if (!readChapters.includes(chapterId)) { readChapters.push(chapterId); localStorage.setItem('readChapters', JSON.stringify(readChapters)); } localStorage.setItem('lastReadChapter', chapterId); } function initAutoHide() { const header = document.getElementById('readerHeader'); const footer = document.getElementById('readerFooter'); let lastScrollY = window.scrollY; let ticking = false; window.addEventListener('scroll', () => { if (!ticking) { window.requestAnimationFrame(() => { const currentScrollY = window.scrollY; if (currentScrollY > lastScrollY && currentScrollY > 100) { // 向下滚动,隐藏 header?.classList.add('hidden'); footer?.classList.add('hidden'); } else { // 向上滚动,显示 header?.classList.remove('hidden'); footer?.classList.remove('hidden'); } lastScrollY = currentScrollY; ticking = false; }); ticking = true; } }); } // ==================== 工具函数 ==================== function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } // ==================== 回到顶部按钮 ==================== function initScrollTop() { const scrollTopBtn = document.getElementById('scrollTop'); if (!scrollTopBtn) return; // 点击回到顶部 scrollTopBtn.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); // 滚动时显示/隐藏按钮 const toggleVisibility = () => { if (window.pageYOffset > 300) { scrollTopBtn.style.opacity = '1'; scrollTopBtn.style.visibility = 'visible'; } else { scrollTopBtn.style.opacity = '0'; scrollTopBtn.style.visibility = 'hidden'; } }; // 初始状态隐藏 scrollTopBtn.style.opacity = '0'; scrollTopBtn.style.visibility = 'hidden'; scrollTopBtn.style.transition = 'opacity 0.3s ease, visibility 0.3s ease'; window.addEventListener('scroll', throttle(toggleVisibility, 100)); } // ==================== 独立章节页面初始化 ==================== async function initChapterReader() { // 确保数据已加载 if (!isDataLoaded) { await loadChaptersData(); } const openSidebar = document.getElementById('openSidebar'); const sidebarClose = document.getElementById('sidebarClose'); const readerSidebar = document.getElementById('readerSidebar'); const overlay = document.getElementById('overlay'); const progressFill = document.getElementById('progressFill'); const data = window.chaptersData || chaptersData; // 动态计算进度条(基于总章节数) if (progressFill) { const totalChapters = data.length; const currentChapterMatch = window.location.pathname.match(/chapter-(\d+)\.html/); const currentChapter = currentChapterMatch ? parseInt(currentChapterMatch[1]) : 1; const progress = Math.round((currentChapter / totalChapters) * 100); progressFill.style.width = progress + '%'; } // 生成侧边栏章节列表 if (readerSidebar) { const sidebarChapters = document.getElementById('sidebarChapters'); if (sidebarChapters) { const currentChapterMatch = window.location.pathname.match(/chapter-(\d+)\.html/); const currentChapter = currentChapterMatch ? parseInt(currentChapterMatch[1]) : 1; sidebarChapters.innerHTML = data.map(chapter => ` ${chapter.id} ${chapter.title} `).join(''); } } if (openSidebar && readerSidebar && overlay) { openSidebar.addEventListener('click', () => { readerSidebar.classList.add('active'); overlay.classList.add('active'); }); } if (sidebarClose && readerSidebar && overlay) { sidebarClose.addEventListener('click', () => { readerSidebar.classList.remove('active'); overlay.classList.remove('active'); }); } if (overlay && readerSidebar) { overlay.addEventListener('click', () => { readerSidebar.classList.remove('active'); overlay.classList.remove('active'); }); } } // ==================== 阅读器设置 ==================== function initReaderSettings() { const settingsBtn = document.getElementById('readerSettings'); const settingsPanel = document.getElementById('settingsPanel'); if (!settingsBtn || !settingsPanel) return; // 切换设置面板显示 settingsBtn.addEventListener('click', (e) => { e.stopPropagation(); settingsPanel.classList.toggle('active'); }); // 点击外部关闭面板 document.addEventListener('click', (e) => { if (!settingsPanel.contains(e.target) && e.target !== settingsBtn) { settingsPanel.classList.remove('active'); } }); // 初始化主题 initThemeSettings(); // 初始化字体大小 initFontSize(); // 初始化行间距 initLineHeight(); // 初始化阅读宽度 initReadWidth(); } // 主题设置 function initThemeSettings() { const themeOptions = document.querySelectorAll('.theme-option'); const savedTheme = localStorage.getItem('readerTheme') || 'dark'; // 应用保存的主题 document.documentElement.setAttribute('data-theme', savedTheme); // 设置激活状态 themeOptions.forEach(option => { if (option.dataset.theme === savedTheme) { option.classList.add('active'); } option.addEventListener('click', () => { const theme = option.dataset.theme; document.documentElement.setAttribute('data-theme', theme); localStorage.setItem('readerTheme', theme); themeOptions.forEach(o => o.classList.remove('active')); option.classList.add('active'); }); }); } // 字体大小设置 function initFontSize() { const decreaseBtn = document.getElementById('fontDecrease'); const increaseBtn = document.getElementById('fontIncrease'); const fontValue = document.getElementById('fontValue'); const content = document.querySelector('.chapter-article') || document.getElementById('chapterContent'); if (!content) return; const sizes = ['14px', '16px', '18px', '20px', '22px', '24px']; let currentSize = localStorage.getItem('readerFontSize') || '18px'; // 应用保存的字体大小 content.style.fontSize = currentSize; if (fontValue) fontValue.textContent = currentSize; decreaseBtn?.addEventListener('click', () => { const index = sizes.indexOf(currentSize); if (index > 0) { currentSize = sizes[index - 1]; content.style.fontSize = currentSize; if (fontValue) fontValue.textContent = currentSize; localStorage.setItem('readerFontSize', currentSize); } }); increaseBtn?.addEventListener('click', () => { const index = sizes.indexOf(currentSize); if (index < sizes.length - 1) { currentSize = sizes[index + 1]; content.style.fontSize = currentSize; if (fontValue) fontValue.textContent = currentSize; localStorage.setItem('readerFontSize', currentSize); } }); } // 行间距设置 function initLineHeight() { const lineHeightBtns = document.querySelectorAll('.line-height-btn'); const content = document.querySelector('.chapter-article') || document.getElementById('chapterContent'); if (!content) return; const savedLineHeight = localStorage.getItem('readerLineHeight') || '1.8'; content.style.lineHeight = savedLineHeight; lineHeightBtns.forEach(btn => { if (btn.dataset.value === savedLineHeight) { btn.classList.add('active'); } btn.addEventListener('click', () => { const value = btn.dataset.value; content.style.lineHeight = value; localStorage.setItem('readerLineHeight', value); lineHeightBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); }); }); } // 阅读宽度设置 function initReadWidth() { const widthBtns = document.querySelectorAll('.width-btn'); const content = document.querySelector('.reader-content'); if (!content) return; const savedWidth = localStorage.getItem('readerWidth') || 'medium'; applyWidth(content, savedWidth); widthBtns.forEach(btn => { if (btn.dataset.width === savedWidth) { btn.classList.add('active'); } btn.addEventListener('click', () => { const width = btn.dataset.width; applyWidth(content, width); localStorage.setItem('readerWidth', width); widthBtns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); }); }); } function applyWidth(content, width) { const widths = { narrow: '680px', medium: '800px', wide: '100%' }; content.style.maxWidth = widths[width] || widths.medium; }