jianzhihuixiang/alacarte-novel-website/chapters/chapter-85.5.html

905 lines
33 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>剑神觉醒 - 阿拉德:剑之回响</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-primary: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
--text-primary: #e0e0e0;
--text-secondary: #888;
--accent-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--border-color: rgba(255,255,255,0.1);
--btn-bg: rgba(255,255,255,0.1);
--btn-hover: rgba(255,255,255,0.2);
}
[data-theme="light"] {
--bg-primary: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
--text-primary: #333;
--text-secondary: #666;
--border-color: rgba(0,0,0,0.1);
--btn-bg: rgba(0,0,0,0.05);
--btn-hover: rgba(0,0,0,0.1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Noto Serif SC', serif;
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.8;
min-height: 100vh;
transition: all 0.3s ease;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
padding-bottom: 120px;
}
/* 顶部导航 */
.top-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.3);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--border-color);
z-index: 1000;
padding: 10px 20px;
}
.top-nav-content {
max-width: 800px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-link {
color: var(--text-primary);
text-decoration: none;
font-family: 'Noto Sans SC', sans-serif;
font-size: 14px;
padding: 8px 16px;
background: var(--btn-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
transition: all 0.3s ease;
}
.nav-link:hover {
background: var(--btn-hover);
}
.chapter-header {
text-align: center;
padding: 80px 0 40px;
border-bottom: 1px solid var(--border-color);
margin-bottom: 40px;
}
.chapter-number {
font-size: 14px;
color: var(--text-secondary);
letter-spacing: 4px;
text-transform: uppercase;
margin-bottom: 10px;
}
.chapter-title {
font-size: 32px;
font-weight: 700;
background: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 20px;
}
.chapter-meta {
font-size: 14px;
color: var(--text-secondary);
}
.chapter-content {
font-size: 18px;
line-height: 2;
text-align: justify;
}
.chapter-content p {
margin-bottom: 1.5em;
text-indent: 2em;
}
.chapter-content p:first-of-type::first-letter {
font-size: 3em;
float: left;
line-height: 1;
margin-right: 8px;
margin-top: -5px;
background: var(--accent-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 700;
}
/* 固定底部导航 */
.fixed-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: rgba(0,0,0,0.3);
backdrop-filter: blur(10px);
border-top: 1px solid var(--border-color);
z-index: 1000;
padding: 15px 20px;
}
.fixed-nav-content {
max-width: 800px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.nav-btn {
padding: 12px 24px;
background: var(--btn-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
color: var(--text-primary);
text-decoration: none;
transition: all 0.3s ease;
font-family: 'Noto Sans SC', sans-serif;
font-size: 14px;
cursor: pointer;
}
.nav-btn:hover {
background: var(--btn-hover);
transform: translateY(-2px);
}
.nav-btn.disabled {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.nav-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-color: #667eea;
}
/* 右侧滚动按钮 */
.scroll-buttons {
position: fixed;
right: 20px;
bottom: 90px;
display: flex;
flex-direction: column;
gap: 10px;
z-index: 1001;
}
.scroll-btn {
width: 40px;
height: 40px;
border-radius: 8px;
background: var(--btn-bg);
border: 1px solid var(--border-color);
color: var(--text-primary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
transition: all 0.3s ease;
font-family: 'Noto Sans SC', sans-serif;
}
.scroll-btn:hover {
background: var(--btn-hover);
}
/* TTS弹出面板 */
.tts-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 2000;
display: none;
align-items: center;
justify-content: center;
}
.tts-overlay.show {
display: flex;
}
.tts-panel {
background: rgba(30,30,50,0.95);
backdrop-filter: blur(10px);
border-radius: 16px;
border: 1px solid var(--border-color);
padding: 24px;
width: 90%;
max-width: 400px;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
}
[data-theme="light"] .tts-panel {
background: rgba(255,255,255,0.95);
}
.tts-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid var(--border-color);
}
.tts-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
font-family: 'Noto Sans SC', sans-serif;
}
.tts-close {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--btn-bg);
border: 1px solid var(--border-color);
color: var(--text-primary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
transition: all 0.3s ease;
}
.tts-close:hover {
background: var(--btn-hover);
}
.tts-controls {
display: flex;
justify-content: center;
gap: 16px;
margin-bottom: 20px;
}
.tts-btn {
width: 56px;
height: 56px;
border-radius: 50%;
background: var(--btn-bg);
border: 2px solid var(--border-color);
color: var(--text-primary);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
transition: all 0.3s ease;
font-family: 'Noto Sans SC', sans-serif;
}
.tts-btn:hover {
background: var(--btn-hover);
transform: scale(1.1);
}
.tts-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-color: #667eea;
transform: scale(1.1);
}
.tts-progress {
margin-bottom: 20px;
}
.tts-progress-bar {
height: 6px;
background: var(--btn-bg);
border-radius: 3px;
overflow: hidden;
margin-bottom: 8px;
}
.tts-progress-fill {
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
width: 0%;
transition: width 0.1s ease;
}
.tts-time {
font-size: 13px;
color: var(--text-secondary);
font-family: 'Noto Sans SC', sans-serif;
text-align: center;
}
.tts-speed {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
.tts-speed-label {
font-size: 14px;
color: var(--text-secondary);
font-family: 'Noto Sans SC', sans-serif;
}
.tts-speed-select {
padding: 8px 16px;
background: var(--btn-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
color: var(--text-primary);
font-size: 14px;
cursor: pointer;
font-family: 'Noto Sans SC', sans-serif;
}
/* 侧边栏 - 标题固定,内容滚动 */
.sidebar {
position: fixed;
right: 20px;
top: 50%;
transform: translateY(-50%);
background: rgba(0,0,0,0.5);
backdrop-filter: blur(10px);
border-radius: 12px;
border: 1px solid var(--border-color);
width: 200px;
max-height: 70vh;
z-index: 999;
display: flex;
flex-direction: column;
}
.sidebar-title {
font-size: 14px;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 2px;
padding: 15px 20px;
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
}
.sidebar-content {
overflow-y: auto;
padding: 10px 20px 20px;
flex: 1;
scrollbar-width: thin;
scrollbar-color: transparent transparent;
transition: scrollbar-color 0.3s ease;
}
.sidebar-content:hover {
scrollbar-color: rgba(255,255,255,0.3) transparent;
}
.sidebar-content::-webkit-scrollbar {
width: 6px;
}
.sidebar-content::-webkit-scrollbar-track {
background: transparent;
}
.sidebar-content::-webkit-scrollbar-thumb {
background: transparent;
border-radius: 3px;
transition: background 0.3s ease;
}
.sidebar-content:hover::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.3);
}
.sidebar-content:hover::-webkit-scrollbar-thumb:hover {
background: rgba(255,255,255,0.5);
}
[data-theme="light"] .sidebar-content:hover {
scrollbar-color: rgba(0,0,0,0.3) transparent;
}
[data-theme="light"] .sidebar-content:hover::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.3);
}
[data-theme="light"] .sidebar-content:hover::-webkit-scrollbar-thumb:hover {
background: rgba(0,0,0,0.5);
}
.sidebar-chapter {
display: block;
padding: 8px 0;
color: #aaa;
text-decoration: none;
font-size: 13px;
border-bottom: 1px solid rgba(255,255,255,0.05);
transition: all 0.3s ease;
line-height: 1.5;
}
.sidebar-chapter:hover {
color: #667eea;
}
.sidebar-chapter.current {
color: #667eea;
font-weight: 600;
}
@media (max-width: 1200px) {
.sidebar {
display: none;
}
}
@media (max-width: 600px) {
.chapter-title {
font-size: 24px;
}
.chapter-content {
font-size: 16px;
}
.fixed-nav-content {
gap: 8px;
}
.nav-btn {
padding: 10px 12px;
font-size: 12px;
}
.scroll-buttons {
display: none;
}
.tts-panel {
padding: 20px;
width: 95%;
}
.tts-btn {
width: 48px;
height: 48px;
font-size: 18px;
}
.tts-title {
font-size: 16px;
}
}
/* 滚动条样式 */
.sidebar::-webkit-scrollbar {
width: 4px;
}
.sidebar::-webkit-scrollbar-track {
background: transparent;
}
.sidebar::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.2);
border-radius: 2px;
}
</style>
</head>
<body>
<!-- 顶部导航 -->
<nav class="top-nav">
<div class="top-nav-content">
<a href="../index.html" class="nav-link">返回首页</a>
<button class="nav-link" id="themeToggle">切换主题</button>
</div>
</nav>
<div class="container">
<header class="chapter-header">
<div class="chapter-number">Chapter 85.5</div>
<h1 class="chapter-title">{{CHAPTER_TITLE}}</h1>
</header>
<article class="chapter-content">
<p>克雷发电站的战斗结束后,林克独自坐在一块岩石上,望着远处的安徒恩。</p>
<p>他的手轻轻抚摸着细雪之舞,冰蓝色的光芒在剑身上流转。但他的眉头却紧锁着。</p>
<p>"怎么了?"米娅走过来,关切地问道,"还在为刚才的战斗担心吗?"</p>
<p>"不是。"林克摇头,"我只是...感觉到自己的力量到了瓶颈。"</p>
<p>"瓶颈?"</p>
<p>"嗯。"林克看向天空中的安徒恩,"那个大家伙,我能感觉到它的力量远远超过我。即使我现在是剑圣,面对真正的使徒...还是不够。"</p>
<p>米娅沉默了,她知道林克说的是实话。安徒恩是第七使徒,它的体型如同山脉,力量深不可测。</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>"也许...你需要突破。"梅尔文走过来,手中拿着一份资料,"我研究过你的战斗数据,你的剑气已经达到了剑圣的巅峰,但...似乎无法更进一步。"</p>
<p>"更进一步?"林克问道。</p>
<p>"剑圣之上,是剑神。"梅尔文说道,"那是传说中只有极少数人能达到的境界。据我所知,目前阿拉德大陆的剑神,一只手都数得过来。"</p>
<p>"索德罗斯...还有那位传说中的剑圣..."林克喃喃道。</p>
<p>"但你和他们不同。"梅尔文说道,"你的剑心更加纯粹,你追求的不是力量本身,而是...保护。"</p>
<p>他拍了拍林克的肩膀:"也许,这就是你突破的关键。"</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>当晚,林克独自在营地外冥想。</p>
<p>他闭上眼睛,将意识沉浸在剑的世界中。无数剑招在他脑海中闪过——里鬼剑术、流心、拔刀斩、破军升龙击、猛龙断空斩、幻影剑舞、极·鬼剑术...</p>
<p>每一个招式他都烂熟于心,但...似乎还缺少什么。</p>
<p>"剑圣的极致...是什么?"他喃喃自语。</p>
<p>"是心。"</p>
<p>一个陌生的声音响起。林克睁开眼睛,看到一个银色长发的男子站在不远处。</p>
<p>那是一个穿着传统剑士服饰的男子,腰间别着一把太刀,气息深邃如海。</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>"你是谁?"林克警觉地问。</p>
<p>"一个路过的剑客。"男子微笑着走近,"感受到这里有一股纯粹的剑气,就来看看。"</p>
<p>他打量着林克:"你是林克?击败兰蒂卢斯的天界英雄?"</p>
<p>"是的。"林克点头,"你认识我?"</p>
<p>"你的名声已经传遍阿拉德大陆了。"男子说道,"一个从洛兰出发的冒险家,一路成长为剑圣,击败卡勒特,拯救天界...很精彩的故事。"</p>
<p>他坐在林克身边:"但你的剑...遇到了瓶颈。"</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>"你怎么知道?"林克惊讶。</p>
<p>"我能感受到。"男子说道,"你的剑气很强,但...缺少灵魂。"</p>
<p>"灵魂?"</p>
<p>"剑圣和剑神的区别。"男子看向天空,"剑圣是剑术的极致,剑神是剑心的极致。"</p>
<p>"你是剑神?"林克问道。</p>
<p>男子摇头:"算是吧。我的老师索德罗斯追求了一生的剑神境界,我...只是追随他的脚步。"</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>林克看着眼前的神秘剑客:"您能指导我吗?"</p>
<p>"指导谈不上。"男子说道,"但我可以和你切磋。"</p>
<p>他站起身,拔出腰间的太刀:"我会压制自己的实力,用剑圣级别的力量和你战斗。如果你能领悟剑神的境界,自然会突破。"</p>
<p>"好。"林克也站起身,握紧细雪之舞。</p>
<p>"对了,还没自我介绍。"男子微笑,"我叫西岚。"</p>
<p>"西岚..."林克记住了这个名字。</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>战斗开始。</p>
<p>西岚的剑如同流水,灵动而凌厉。每一剑都恰到好处,封住林克的退路。</p>
<p>林克奋力招架,但渐渐落入下风。</p>
<p>"你的剑术已经足够了。"西岚一边攻击一边说,"但你的心...还不够。"</p>
<p>"什么意思?"</p>
<p>"你太在意胜负了。"西岚说道,"真正的剑神,不会在意胜负,只会...顺应本心。"</p>
<p>他一剑刺出,林克连忙格挡,但还是被震退数步。</p>
<p>"顺应本心..."林克喃喃道。</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>他闭上眼睛,脑海中浮现出那些少女的面容——赛丽亚温柔的笑容、敏泰活泼的身影、帕丽丝爽朗的性格、莎兰优雅的气质...</p>
<p>"我想要的...不是战胜你。"林克轻声说道,"我想要的...是保护她们。"</p>
<p>他的剑气突然发生了变化。</p>
<p>原本金色的剑气中,多了一层淡淡的白光。那不是圣光,不是魔力,而是...纯粹的意志。</p>
<p>"这是..."西岚眼中闪过惊讶,"剑意实体化?!"</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>林克睁开眼睛,他的瞳孔中似乎有星辰在闪烁。</p>
<p>"我明白了。"他说道,"剑不是武器,是意志的延伸。我的意志是保护,所以我的剑...会为保护而存在。"</p>
<p>他举起细雪之舞,冰蓝色的剑身上出现了前所未有的光芒。</p>
<p>"极·神剑术——万剑归宗。"</p>
<p>刹那间,无数剑气从林克身上爆发!那些剑气不是混乱的,而是有秩序地环绕在他周围,如同忠诚的卫士。</p>
<p>西岚收起太刀,脸上露出欣慰的笑容:"恭喜你,林克。"</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>他伸出手:"从今天起,你就是真正的剑神了。"</p>
<p>林克感受着体内涌动的力量,那是一种全新的感觉。</p>
<p>剑圣和剑神的区别,不是力量的大小,而是...控制。</p>
<p>剑圣是控制剑气,剑神是让剑气成为自己的一部分。他的意志延伸到哪里,剑气就会跟随到哪里。</p>
<p>"谢谢你,西岚前辈。"林克说道。</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>"不用谢我。"西岚摇头,"这是你自己领悟的。我只是...指了个方向。"</p>
<p>他看向远处沉睡的安徒恩:"去吧,林克。用你的新力量,击败那个大家伙。"</p>
<p>"我会的。"林克握紧细雪之舞。</p>
<p>西岚的身影开始消散,像风一样消失。</p>
<p>"后会有期,林克。期待有一天...能和你真正交手。"</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>第二天清晨,众人继续前进。</p>
<p>"林克大哥,你看起来不一样了。"米娅好奇地看着他,"好像...更强了?"</p>
<p>"嗯。"林克微笑,"我突破了。"</p>
<p>"突破了?"梅尔文惊讶,"你的气息确实变了...难道是..."</p>
<p>"剑神。"林克说道,"我现在是剑神了。"</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p>"什么?!"梅尔文瞪大了眼睛,"你才多少级?!怎么可能突破到剑神境界?!"</p>
<p>"大概是...75级?"林克不太确定。</p>
<p>"75级就剑神"梅尔文更加震惊,"这简直是天才中的天才!"</p>
<p>泽丁和马琳也惊讶地看着林克,她们知道这意味着什么。</p>
<p>"走吧。"林克看向远方,"普鲁兹发电站在等着我们。"</p>
<p>他走在最前方,细雪之舞在腰间轻轻颤动,仿佛在欢呼雀跃。</p>
<p>剑神觉醒,新的力量,新的征程。</p>
<p>安徒恩...你的末日到了。</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>
<p style="text-align: center; color: var(--text-secondary); text-indent: 0; margin-top: 2em;">(第八十五点五章完)</p>
</article>
</div>
<!-- 固定底部导航 -->
<nav class="fixed-nav">
<div class="fixed-nav-content">
<a href="chapter-84.5.html" class="nav-btn ">上一章</a>
<a href="../chapters.html" class="nav-btn">目录</a>
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
<a href="chapter-85.51.html" class="nav-btn ">下一章</a>
</div>
</nav>
<!-- 右侧滚动按钮 -->
<div class="scroll-buttons">
<button class="scroll-btn" id="scrollTop" title="回到顶部"></button>
<button class="scroll-btn" id="scrollBottom" title="回到底部"></button>
</div>
<!-- TTS弹出面板 -->
<div class="tts-overlay" id="ttsOverlay">
<div class="tts-panel">
<div class="tts-header">
<div class="tts-title">🔊 语音朗读</div>
<button class="tts-close" id="ttsClose">×</button>
</div>
<div class="tts-controls">
<button class="tts-btn" id="ttsPlay" title="播放"></button>
<button class="tts-btn" id="ttsPause" title="暂停"></button>
<button class="tts-btn" id="ttsStop" title="停止"></button>
</div>
<div class="tts-progress">
<div class="tts-progress-bar">
<div class="tts-progress-fill" id="ttsProgressFill"></div>
</div>
<div class="tts-time" id="ttsTime">0 / 0 句</div>
</div>
<div class="tts-speed">
<span class="tts-speed-label">朗读速度:</span>
<select class="tts-speed-select" id="ttsSpeed">
<option value="0.5">慢速 (0.5x)</option>
<option value="0.75">较慢 (0.75x)</option>
<option value="1" selected>正常 (1x)</option>
<option value="1.25">较快 (1.25x)</option>
<option value="1.5">快速 (1.5x)</option>
<option value="2">极速 (2x)</option>
</select>
</div>
</div>
</div>
<!-- 侧边栏章节导航 -->
<aside class="sidebar">
<div class="sidebar-title">章节导航</div>
<div class="sidebar-content" id="sidebarContent">
</div>
</aside>
<script>
// 记录阅读进度
let readChapters = JSON.parse(localStorage.getItem('readChapters') || '[]');
if (!readChapters.includes(85.5)) {
readChapters.push({{CHAPTER_ID}});
localStorage.setItem('readChapters', JSON.stringify(readChapters));
}
// 主题切换
const themeToggle = document.getElementById('themeToggle');
const savedTheme = localStorage.getItem('theme') || 'dark';
document.documentElement.setAttribute('data-theme', savedTheme);
themeToggle.textContent = savedTheme === 'dark' ? '浅色' : '深色';
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);
themeToggle.textContent = newTheme === 'dark' ? '浅色' : '深色';
});
// 滚动到顶部
document.getElementById('scrollTop').addEventListener('click', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
// 滚动到底部
document.getElementById('scrollBottom').addEventListener('click', () => {
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
});
// 页面加载时,侧边栏自动滚动到当前章节
window.addEventListener('load', () => {
const sidebarContent = document.getElementById('sidebarContent');
const currentChapter = sidebarContent.querySelector('.current');
if (currentChapter) {
currentChapter.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
});
// ========== TTS弹出面板 ==========
const ttsOverlay = document.getElementById('ttsOverlay');
const ttsToggleBtn = document.getElementById('ttsToggleBtn');
const ttsClose = document.getElementById('ttsClose');
// 打开TTS面板
ttsToggleBtn.addEventListener('click', () => {
ttsOverlay.classList.add('show');
initTTS();
});
// 关闭TTS面板
ttsClose.addEventListener('click', () => {
ttsOverlay.classList.remove('show');
});
// 点击遮罩关闭
ttsOverlay.addEventListener('click', (e) => {
if (e.target === ttsOverlay) {
ttsOverlay.classList.remove('show');
}
});
// ========== TTS语音朗读功能 ==========
let ttsSynth = window.speechSynthesis;
let ttsUtterance = null;
let ttsText = '';
let ttsSentences = [];
let ttsCurrentIndex = 0;
let ttsIsPlaying = false;
let ttsIsPaused = false;
let ttsSpeed = 1;
// 初始化:提取章节内容
function initTTS() {
const contentEl = document.querySelector('.chapter-content');
if (!contentEl) return;
// 获取所有段落文本清理HTML标签
ttsText = contentEl.innerText || contentEl.textContent;
// 分割成句子(中文按句号、问号、感叹号分割)
ttsSentences = ttsText.match(/[^。!?\n]+[。!?\n]+|[^。!?\n]+$/g) || [ttsText];
ttsSentences = ttsSentences.filter(s => s.trim().length > 0);
updateTTSProgress();
}
// 更新进度显示
function updateTTSProgress() {
const total = ttsSentences.length;
const current = ttsCurrentIndex;
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
const percent = total > 0 ? (current / total * 100) : 0;
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
}
// 播放当前句子
function playCurrentSentence() {
if (ttsCurrentIndex >= ttsSentences.length) {
stopTTS();
return;
}
const text = ttsSentences[ttsCurrentIndex].trim();
ttsUtterance = new SpeechSynthesisUtterance(text);
ttsUtterance.lang = 'zh-CN';
ttsUtterance.rate = ttsSpeed;
// 尝试选择中文语音
const voices = ttsSynth.getVoices();
const zhVoice = voices.find(v => v.lang.includes('zh') || v.lang.includes('CN'));
if (zhVoice) {
ttsUtterance.voice = zhVoice;
}
ttsUtterance.onend = () => {
if (ttsIsPlaying && !ttsIsPaused) {
ttsCurrentIndex++;
updateTTSProgress();
playCurrentSentence();
}
};
ttsUtterance.onerror = (e) => {
console.error('TTS error:', e);
if (ttsIsPlaying) {
ttsCurrentIndex++;
updateTTSProgress();
playCurrentSentence();
}
};
ttsSynth.speak(ttsUtterance);
}
// 播放
function playTTS() {
if (ttsSentences.length === 0) {
initTTS();
}
if (ttsIsPaused) {
ttsSynth.resume();
ttsIsPaused = false;
} else {
ttsIsPlaying = true;
playCurrentSentence();
}
document.getElementById('ttsPlay').classList.add('active');
document.getElementById('ttsPause').classList.remove('active');
ttsToggleBtn.classList.add('active');
}
// 暂停
function pauseTTS() {
if (ttsIsPlaying) {
ttsSynth.pause();
ttsIsPaused = true;
document.getElementById('ttsPlay').classList.remove('active');
document.getElementById('ttsPause').classList.add('active');
ttsToggleBtn.classList.remove('active');
}
}
// 停止
function stopTTS() {
ttsSynth.cancel();
ttsIsPlaying = false;
ttsIsPaused = false;
ttsCurrentIndex = 0;
updateTTSProgress();
document.getElementById('ttsPlay').classList.remove('active');
document.getElementById('ttsPause').classList.remove('active');
ttsToggleBtn.classList.remove('active');
}
// 设置速度
function setTTSSpeed(speed) {
ttsSpeed = parseFloat(speed);
// 如果正在播放,需要重新开始当前句子
if (ttsIsPlaying && !ttsIsPaused) {
ttsSynth.cancel();
playCurrentSentence();
}
}
// 绑定事件
document.getElementById('ttsPlay').addEventListener('click', playTTS);
document.getElementById('ttsPause').addEventListener('click', pauseTTS);
document.getElementById('ttsStop').addEventListener('click', stopTTS);
document.getElementById('ttsSpeed').addEventListener('change', (e) => setTTSSpeed(e.target.value));
// 加载语音列表(某些浏览器需要异步加载)
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = initTTS;
}
// 页面离开时停止播放
window.addEventListener('beforeunload', stopTTS);
</script>
</body>
</html>