762 lines
36 KiB
JavaScript
762 lines
36 KiB
JavaScript
// 章节数据
|
||
const chaptersData = [
|
||
{ id: 1, title: "第一章:洛兰", subtitle: "初入艾尔文防线", desc: "林克初到艾尔文防线,在洛兰森林遭遇哥布林,被阿甘左救下。", status: "已完结", date: "2026-03-20" },
|
||
{ id: 2, title: "第二章:洛兰深处", subtitle: "哥布林头目", desc: "林克在阿甘左指导下修炼剑术,深入洛兰深处击败哥布林头目。", status: "已完结", date: "2026-03-20" },
|
||
{ id: 3, title: "第三章:幽暗密林", subtitle: "猫妖的伏击", desc: "林克进入格兰之森深处的幽暗密林,遭遇猫妖伏击,历经苦战突破重围。", status: "已完结", date: "2026-03-23" },
|
||
{ id: 4, title: "第四章:幽暗密林深处", subtitle: "猫妖王", desc: "林克再次进入幽暗密林深处,遭遇猫妖王,揭开卡赞诅咒的部分真相。", status: "已完结", date: "2026-03-23" },
|
||
{ id: 5, title: "第五章:雷鸣废墟", subtitle: "闪电哥布林", desc: "林克挑战雷鸣废墟,遭遇闪电哥布林,经历雷电危机。", status: "已完结", date: "2026-03-23" },
|
||
{ id: 6, title: "第六章:格拉卡", subtitle: "牛头王萨乌塔", desc: "林克进入格拉卡,面对强大的牛头王萨乌塔,激战突破自身极限。", status: "已完结", date: "2026-03-23" },
|
||
{ id: 7, title: "第七章:烈焰格拉卡", subtitle: "火女彼诺修", desc: "林克挑战火女彼诺修,她的血之诅咒让战斗更加凶险。", status: "已完结", date: "2026-03-23" },
|
||
{ id: 8, title: "第八章:冰霜幽暗密林", subtitle: "冰女克拉赫", desc: "为寻找赛丽亚身世线索,林克前往冰霜幽暗密林挑战冰女克拉赫。", status: "已完结", date: "2026-03-23" },
|
||
{ id: 9, title: "第九章:转职之路", subtitle: "选择剑魂", desc: "林克前往赫顿玛尔寻找GSD,了解四大转职方向,坚定选择剑魂之道。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 10, title: "第十章:暗黑雷鸣废墟", subtitle: "转职试炼", desc: "林克在暗黑雷鸣废墟完成转职试炼,在死亡气息中验证自己的剑道。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 11, title: "第十一章:剑魂转职仪式", subtitle: "武器大师", desc: "GSD为林克举行正式转职仪式,详细讲解剑魂武器大师特性。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 12, title: "第十二章:西海岸", subtitle: "天空之城入口", desc: "林克前往西海岸了解天空之城背景,GSD决定同行。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 13, title: "第十三章:龙人之塔", subtitle: "GSD带队", desc: "GSD亲自带队刷龙人之塔,展现'开挂瞎子'的恐怖实力。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 14, title: "第十四章:人偶玄关", subtitle: "觉醒技能", desc: "GSD带队刷人偶玄关,展现阿修罗觉醒技能'暗天波动眼'。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 15, title: "第十五章:石巨人塔", subtitle: "独立挑战", desc: "林克和赛丽亚独立挑战石巨人塔,面对黄金巨人普拉塔尼。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 16, title: "第十六章:黑暗玄廊", subtitle: "光明与黑暗", desc: "回赫顿玛尔交任务,探索黑暗玄廊,击败天之驱逐者。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 17, title: "第十七章:城主宫殿", subtitle: "光之城主", desc: "面对光之城主赛格哈特,净化光之核心,揭晓晨曦的来历。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 18, title: "第十八章:番外·悬空城", subtitle: "光之骑士", desc: "探索天空之城最深处,恢复晨曦的光芒,获得光之骑士称号。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 19, title: "第十九章:天帷巨兽·神殿外围", subtitle: "使徒的呼唤", desc: "前往天帷巨兽调查GBL教异变,遭遇第二使徒罗特斯的精神控制。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 20, title: "第二十章:树精丛林", subtitle: "禁忌实验", desc: "探索GBL教的秘密实验室,发现使徒融合实验,击败树精王。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 21, title: "第二十一章:炼狱", subtitle: "火焰试炼", desc: "穿越夜叉栖息的炼狱,击败夜叉王,晨曦恢复七成。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 22, title: "第二十二章:西海岸的闲暇", subtitle: "支线与成长", desc: "返回西海岸休整,完成支线任务,与卡坤互动,学会拔刀斩。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 23, title: "第二十三章:极昼", subtitle: "天空之巅", desc: "赛丽亚因精神污染离队,林克独自挑战极昼,击败EX多尼尔。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 24, title: "第二十四章:第一脊椎", subtitle: "圣光降临", desc: "圣骑士艾伦加入队伍,联手击败巨型黑章鱼,晨曦恢复九成。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 25, title: "第二十五章:赫顿玛尔的准备", subtitle: "天界科技", desc: "凯丽制造抗精神干扰手环,为最终决战做准备。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 26, title: "第二十六章:第二脊椎", subtitle: "使徒决战", desc: "最终BOSS罗特斯,晨曦完全觉醒,天帷巨兽篇完结。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 27, title: "第二十七章:重逢的温柔", subtitle: "赛丽亚线", desc: "林克与赛丽亚久别重逢,两人感情突破,正式确立关系。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 28, title: "第二十八章:暗精灵的委托", subtitle: "后宫建立", desc: "莎兰暧昧互动,奥菲利亚表白加入后宫,前往暗黑城新任务。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 29, title: "第二十九章:阿法利亚营地", subtitle: "暗黑城篇开启", desc: "抵达阿法利亚营地,接受克伦特委托,后宫修罗场帐篷同眠。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 30, title: "第三十章:浅栖之地", subtitle: "寻找摩根", desc: "探索浅栖之地,击败怨恨之摩根,发现夏普伦长老的阴谋。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 31, title: "第三十一章:蜘蛛洞穴", subtitle: "猛龙断空斩", desc: "首次实战使用猛龙断空斩,击败BOSS艾克洛索,突破Lv.50。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 32, title: "第三十二章:克伦特的委托", subtitle: "四把钥匙", desc: "返回营地汇报,接受克伦特委托,准备前往暗精灵墓地收集四把钥匙。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 33, title: "第三十三章:暗精灵墓地·左翼守卫", subtitle: "奥菲利亚线", desc: "击败火焰骷髅将军获得第一把钥匙,奥菲利亚献身,后宫正式确立。", status: "已完结", date: "2026-03-24" },
|
||
{ id: 34, title: "第三十四章:暗精灵墓地·剩余三将军", subtitle: "三战连捷", desc: "连续击败冰冻、速度、剧毒三位骷髅将军,集齐四把钥匙。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 35, title: "第三十五章:邪龙斯皮兹", subtitle: "远古邪龙", desc: "用四把钥匙解开封印,击败暗精灵墓地最终BOSS邪龙斯皮兹,彻底消灭远古邪恶。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 36, title: "第三十六章:莎兰的探望", subtitle: "暧昧之夜", desc: "击败邪龙后回到西海岸,莎兰深夜单独约见林克,两人关系更进一步。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 37, title: "第三十七章:暗影迷宫·影子剑士", subtitle: "剑士对决", desc: "进入暗影迷宫挑战影子剑士刹影,林克以光之剑对决暗影之剑,最终净化刹影。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 38, title: "第三十八章:熔岩穴·泰坦之怒", subtitle: "熔岩巨人", desc: "挑战熔岩穴深处的熔岩巨人泰坦,林克找到核心弱点,以龙之力彻底消灭怪物。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 39, title: "第三十九章:暗黑城入口·无头骑士", subtitle: "幻影剑舞", desc: "林克向GSD学习45级技能幻影剑舞,挑战无头骑士,以新剑技击败强敌。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 40, title: "第四十章:万年雪山·冰心少年", subtitle: "冰心查理", desc: "抵达班图族营地,购买新武器细雪之舞,遭遇被冰龙侵蚀的查理,团队配合制服暴走。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 41, title: "第四十一章:敏泰的加入", subtitle: "队伍调整", desc: "艾伦退场返回赫顿玛尔,敏泰加入队伍,奥菲利亚转为后勤,新队伍前往利库天井。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 42, title: "第四十二章:山脊·野兽师", subtitle: "后宫互动", desc: "挑战山脊BOSS野兽师鲁乌格,赛丽亚受伤,深夜教导敏泰如何成为林克的女人。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 43, title: "第四十三章:番外·雪山温泉", subtitle: "敏泰献身", desc: "班图族温泉中,赛丽亚引导敏泰向林克献身,三人共度温馨之夜。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 44, title: "第四十四章:白色废墟", subtitle: "雪魈王", desc: "探索白色废墟,遭遇雪魈、冰精灵、被控制的班图族战士,配合击败雪魈王。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 45, title: "第四十五章:冰雪宫殿", subtitle: "冰龙侍从", desc: "深入冰雪宫殿,遭遇冰蜘蛛、冰霜巨人,最终击败冰龙侍从,获得进入巢穴的资格。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 46, title: "第四十六章:布万加修炼场", subtitle: "突破", desc: "在布万加修炼场接受训练,林克领悟山岳剑势,赛丽亚点燃心火,敏泰获得母亲骨杖。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 47, title: "第四十七章:斯卡萨之巢·龙威", subtitle: "初战冰龙", desc: "首次挑战冰龙斯卡萨,巴卡尔三龙崽背景揭露,不敌撤退,奥尔卡、奥菲利亚、查理赶来支援。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 48, title: "第四十八章:斯卡萨之巢·龙陨", subtitle: "雪山篇完结", desc: "全员集结决战冰龙斯卡萨,击破逆鳞,班图族解放,雪山篇落幕。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 49, title: "第四十九章:重返赫顿玛尔", subtitle: "休整", desc: "返回赫顿玛尔休整,与艾伦重逢喝酒,莎兰献身,接受诺斯玛尔异变任务。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 50, title: "第五十章:诺斯玛尔·盗贼团", subtitle: "帕丽丝加入", desc: "到达诺斯玛尔,遭遇被瘟疫控制的盗贼团,救下街霸帕丽丝,奥菲利亚退场回西海岸。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 51, title: "第五十一章:哈穆林·鼠患", subtitle: "鼠王", desc: "深入哈穆林下水道,遭遇变异巨鼠群,配合击败鼠王,获得瘟疫核心晶石。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 52, title: "第五十二章:月光酒馆", subtitle: "长三郎", desc: "探索月光酒馆,遭遇被控制的冒险者,击败BOSS长三郎,获得痛苦之村地图。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 53, title: "第五十三章:番外·帕丽丝的誓言", subtitle: "帕丽丝献身", desc: "废弃别墅中,帕丽丝向林克表白献身,两人成为真正的恋人,赛丽亚大度接纳。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 54, title: "第五十四章:觉醒之路·启程", subtitle: "GSD", desc: "返回赫顿玛尔找GSD,接受觉醒试炼,准备前往比尔马克帝国试验场挑战机械牛。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 55, title: "第五十五章:比尔马克帝国试验场", subtitle: "机械牛", desc: "挑战比尔马克帝国试验场,遭遇各种改造怪物,击败牛头械王获得牛妖之血。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 56, title: "第五十六章:王的遗迹·远古五骑士", subtitle: "五骑士", desc: "挑战王的遗迹,依次击败风、守护、冰、火、光五位远古骑士,获得五大印记。", status: "已完结", date: "2026-03-25" },
|
||
{ id: 57, title: "第五十七章:心灵试炼·剑圣觉醒", subtitle: "觉醒", desc: "完成心灵试炼,战胜心魔,成功觉醒成为剑圣,获得极·鬼剑术。", status: "已完结", date: "2026-03-25" }
|
||
];
|
||
|
||
// DOM加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
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();
|
||
} else if (currentPage === 'reader.html') {
|
||
initReader();
|
||
} else if (currentPage.startsWith('chapter-')) {
|
||
// 独立章节页面初始化
|
||
initChapterReader();
|
||
}
|
||
|
||
// 初始化回到顶部按钮
|
||
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');
|
||
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');
|
||
|
||
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 => `
|
||
<a href="reader.html?id=${chapter.id}" class="chapter-card">
|
||
<div class="chapter-number">${chapter.id}</div>
|
||
<div class="chapter-info">
|
||
<h3 class="chapter-title">${chapter.title}</h3>
|
||
<p class="chapter-desc">${chapter.desc}</p>
|
||
<div class="chapter-meta">
|
||
<span>${chapter.date}</span>
|
||
<span class="chapter-status">${chapter.status}</span>
|
||
</div>
|
||
</div>
|
||
</a>
|
||
`).join('');
|
||
}
|
||
|
||
// ==================== 章节列表页 ====================
|
||
function renderChaptersList() {
|
||
const container = document.getElementById('chaptersList');
|
||
if (!container) return;
|
||
|
||
// 优先使用从JSON动态加载的数据
|
||
const data = window.chaptersData || chaptersData;
|
||
|
||
container.innerHTML = data.map(chapter => `
|
||
<div class="timeline-item" data-chapter="${chapter.id}">
|
||
<div class="timeline-marker"></div>
|
||
<a href="reader.html?id=${chapter.id}" class="timeline-content">
|
||
<div class="timeline-header">
|
||
<h3>${chapter.title}</h3>
|
||
<span class="timeline-date">${chapter.date}</span>
|
||
</div>
|
||
<p class="timeline-subtitle">${chapter.subtitle}</p>
|
||
<p class="timeline-desc">${chapter.desc}</p>
|
||
</a>
|
||
</div>
|
||
`).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}%`;
|
||
}
|
||
|
||
// ==================== 阅读器 ====================
|
||
function initReader() {
|
||
// 获取章节参数
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
const chapterId = parseInt(urlParams.get('chapter')) || 1;
|
||
|
||
// 加载章节内容
|
||
loadChapter(chapterId);
|
||
|
||
// 初始化阅读器设置
|
||
initReaderSettings();
|
||
|
||
// 初始化导航
|
||
initReaderNav(chapterId);
|
||
|
||
// 初始化侧边栏
|
||
initSidebar();
|
||
|
||
// 记录阅读进度
|
||
recordReadingProgress(chapterId);
|
||
|
||
// 自动隐藏头部和底部
|
||
initAutoHide();
|
||
}
|
||
|
||
function loadChapter(chapterId) {
|
||
const chapter = chaptersData.find(c => c.id === chapterId);
|
||
if (!chapter) return;
|
||
|
||
// 更新标题
|
||
document.getElementById('chapterTitle').textContent = chapter.title;
|
||
document.title = `${chapter.title} - 阿拉德:剑之回响`;
|
||
|
||
// 加载内容(模拟)
|
||
const contentEl = document.getElementById('chapterContent');
|
||
contentEl.innerHTML = `
|
||
<div class="chapter-header">
|
||
<h1>${chapter.title}</h1>
|
||
<p class="chapter-subtitle">${chapter.subtitle}</p>
|
||
</div>
|
||
<div class="chapter-body">
|
||
<p class="placeholder-text">正在从飞书文档加载内容...</p>
|
||
<p class="placeholder-text">章节链接:<a href="${getChapterUrl(chapterId)}" target="_blank">在飞书文档中查看</a></p>
|
||
</div>
|
||
<div class="chapter-footer">
|
||
<p>本章完</p>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function getChapterUrl(chapterId) {
|
||
const urls = {
|
||
1: 'https://www.feishu.cn/docx/KBdwdTd8foLLfSxCOmDcNr1LnKe',
|
||
2: 'https://www.feishu.cn/docx/Jt0wdpCwDoq7zKxT2TUc54Hlnmd',
|
||
3: 'https://www.feishu.cn/docx/G0jXdqGzPoVLe9x9S9qcPzTJnsc',
|
||
4: 'https://www.feishu.cn/docx/IckMdiy7cor3fCxap2ic1D40n4d',
|
||
5: 'https://www.feishu.cn/docx/PQBMdL9DQosxe1xsq34czRnFnPc',
|
||
6: 'https://www.feishu.cn/docx/XflsdWsoRoyS5Xxg91VcmkysnlK',
|
||
7: 'https://www.feishu.cn/docx/XL9YdobRMoaSwXxTe23cpuwynkh',
|
||
8: 'https://www.feishu.cn/docx/FJ80dI1A1oCRgIxR6i2cIXESnnd',
|
||
9: 'https://www.feishu.cn/docx/L2tydWs0Bo84h4xE3hbcfl8MnPg',
|
||
10: 'https://www.feishu.cn/docx/Iu2LdfRg2oEP2zxh3kRcqBJ9n1f',
|
||
11: 'https://www.feishu.cn/docx/NEiJdna0Jo1OHEx1s84cbNgBn1e',
|
||
12: 'https://www.feishu.cn/docx/L2j3dt7PQodXFKxSuoScT3BZnkc',
|
||
13: 'https://www.feishu.cn/docx/WHgFdA6NXoyynwxdLmMcjZaPnfd',
|
||
14: 'https://www.feishu.cn/docx/FhoNdTZq8oPs91xrb2Gcg2udnAh'
|
||
};
|
||
return urls[chapterId] || '#';
|
||
}
|
||
|
||
function initReaderSettings() {
|
||
const settingsToggle = document.getElementById('settingsToggle');
|
||
const readerSettings = document.getElementById('readerSettings');
|
||
const overlay = document.getElementById('overlay');
|
||
|
||
if (settingsToggle) {
|
||
settingsToggle.addEventListener('click', () => {
|
||
readerSettings.classList.toggle('active');
|
||
overlay.classList.toggle('active');
|
||
});
|
||
}
|
||
|
||
if (overlay) {
|
||
overlay.addEventListener('click', () => {
|
||
readerSettings.classList.remove('active');
|
||
overlay.classList.remove('active');
|
||
});
|
||
}
|
||
|
||
// 字体大小
|
||
const sizeBtns = document.querySelectorAll('.size-btn');
|
||
sizeBtns.forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
sizeBtns.forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
|
||
const size = btn.dataset.size;
|
||
const sizes = { small: '16px', normal: '18px', large: '20px' };
|
||
document.getElementById('chapterContent').style.fontSize = sizes[size];
|
||
});
|
||
});
|
||
|
||
// 主题
|
||
const themeOptions = document.querySelectorAll('.theme-option');
|
||
themeOptions.forEach(option => {
|
||
option.addEventListener('click', () => {
|
||
themeOptions.forEach(o => o.classList.remove('active'));
|
||
option.classList.add('active');
|
||
|
||
const theme = option.dataset.theme;
|
||
document.documentElement.setAttribute('data-theme', theme);
|
||
localStorage.setItem('theme', theme);
|
||
});
|
||
});
|
||
}
|
||
|
||
function initReaderNav(currentId) {
|
||
const prevBtn = document.getElementById('prevChapter');
|
||
const nextBtn = document.getElementById('nextChapter');
|
||
|
||
if (prevBtn) {
|
||
if (currentId > 1) {
|
||
prevBtn.disabled = false;
|
||
prevBtn.addEventListener('click', () => {
|
||
window.location.href = `reader.html?chapter=${currentId - 1}`;
|
||
});
|
||
} else {
|
||
prevBtn.disabled = true;
|
||
}
|
||
}
|
||
|
||
if (nextBtn) {
|
||
if (currentId < chaptersData.length) {
|
||
nextBtn.disabled = false;
|
||
nextBtn.addEventListener('click', () => {
|
||
window.location.href = `reader.html?chapter=${currentId + 1}`;
|
||
});
|
||
} 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) {
|
||
sidebarChapters.innerHTML = chaptersData.map(chapter => `
|
||
<a href="reader.html?chapter=${chapter.id}" class="sidebar-chapter ${chapter.id === getCurrentChapterId() ? 'active' : ''}">
|
||
<span class="chapter-num">${chapter.id}</span>
|
||
<span class="chapter-name">${chapter.title}</span>
|
||
</a>
|
||
`).join('');
|
||
}
|
||
|
||
if (openSidebar) {
|
||
openSidebar.addEventListener('click', () => {
|
||
readerSidebar.classList.add('active');
|
||
overlay.classList.add('active');
|
||
});
|
||
}
|
||
|
||
if (sidebarClose) {
|
||
sidebarClose.addEventListener('click', () => {
|
||
readerSidebar.classList.remove('active');
|
||
overlay.classList.remove('active');
|
||
});
|
||
}
|
||
}
|
||
|
||
function getCurrentChapterId() {
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
return parseInt(urlParams.get('chapter')) || 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));
|
||
}
|
||
|
||
// ==================== 独立章节页面初始化 ====================
|
||
function initChapterReader() {
|
||
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');
|
||
|
||
// 动态计算进度条(基于总章节数)
|
||
if (progressFill) {
|
||
const totalChapters = chaptersData.length; // 从 chaptersData 获取总章节数
|
||
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 (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');
|
||
|
||
if (!content) return;
|
||
|
||
const sizes = ['14px', '16px', '18px', '20px', '22px', '24px'];
|
||
let currentSize = localStorage.getItem('readerFontSize') || '18px';
|
||
|
||
// 应用保存的字体大小
|
||
content.style.fontSize = currentSize;
|
||
fontValue.textContent = currentSize;
|
||
|
||
decreaseBtn?.addEventListener('click', () => {
|
||
const index = sizes.indexOf(currentSize);
|
||
if (index > 0) {
|
||
currentSize = sizes[index - 1];
|
||
content.style.fontSize = currentSize;
|
||
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;
|
||
fontValue.textContent = currentSize;
|
||
localStorage.setItem('readerFontSize', currentSize);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 行间距设置
|
||
function initLineHeight() {
|
||
const lineHeightBtns = document.querySelectorAll('.line-height-btn');
|
||
const content = document.querySelector('.chapter-article');
|
||
|
||
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;
|
||
}
|