改为弹出式TTS面板,底部导航栏添加朗读按钮
This commit is contained in:
parent
ad37fe5b64
commit
48353ba65d
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -607,6 +606,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-9.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-11.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -617,9 +617,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -629,20 +633,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -733,6 +738,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -761,7 +789,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -821,6 +849,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -830,6 +859,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -842,6 +872,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -865,11 +896,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -620,6 +619,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-99.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-101.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -630,9 +630,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -642,20 +646,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -767,6 +772,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -795,7 +823,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -855,6 +883,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -864,6 +893,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -876,6 +906,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -899,11 +930,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,6 +667,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-100.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-102.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -678,9 +678,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -690,20 +694,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -815,6 +820,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -843,7 +871,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -903,6 +931,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -912,6 +941,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -924,6 +954,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -947,11 +978,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -628,6 +627,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-101.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-103.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -638,9 +638,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -650,20 +654,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -775,6 +780,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -803,7 +831,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -863,6 +891,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -872,6 +901,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,6 +914,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -907,11 +938,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -633,6 +632,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-102.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-104.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -643,9 +643,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -655,20 +659,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -780,6 +785,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -808,7 +836,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -868,6 +896,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -877,6 +906,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -889,6 +919,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -912,11 +943,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -613,6 +612,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-103.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-105.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -623,9 +623,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -635,20 +639,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -760,6 +765,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -788,7 +816,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -848,6 +876,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -857,6 +886,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -869,6 +899,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -892,11 +923,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -597,6 +596,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-104.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-106.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -607,9 +607,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -619,20 +623,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -744,6 +749,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -772,7 +800,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -832,6 +860,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -841,6 +870,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -853,6 +883,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -876,11 +907,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,6 +591,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-105.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-107.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -602,9 +602,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -614,20 +618,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -739,6 +744,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -767,7 +795,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -827,6 +855,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -836,6 +865,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -848,6 +878,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -871,11 +902,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,6 +605,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-106.5.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-108.5.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -616,9 +616,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -628,20 +632,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -693,6 +698,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -721,7 +749,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -781,6 +809,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -790,6 +819,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -802,6 +832,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -825,11 +856,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,6 +658,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-106.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-108.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -669,9 +669,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -681,20 +685,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -806,6 +811,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -834,7 +862,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -894,6 +922,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -903,6 +932,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -915,6 +945,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -938,11 +969,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -658,6 +657,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-107.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-109.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -668,9 +668,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -680,20 +684,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -805,6 +810,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -833,7 +861,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -893,6 +921,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -902,6 +931,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -914,6 +944,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -937,11 +968,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,6 +646,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-108.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-110.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -657,9 +657,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -669,20 +673,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -794,6 +799,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -822,7 +850,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -882,6 +910,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -891,6 +920,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -903,6 +933,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -926,11 +957,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -600,6 +599,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-10.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-12.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -610,9 +610,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -622,20 +626,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -727,6 +732,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -755,7 +783,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -815,6 +843,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -824,6 +853,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -836,6 +866,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -859,11 +890,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -609,6 +608,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-109.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-111.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -619,9 +619,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -631,20 +635,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -756,6 +761,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -784,7 +812,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -844,6 +872,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -853,6 +882,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -865,6 +895,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -888,11 +919,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -607,6 +606,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-110.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-112.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -617,9 +617,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -629,20 +633,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -754,6 +759,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -782,7 +810,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -842,6 +870,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -851,6 +880,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -863,6 +893,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -886,11 +917,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -606,6 +605,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-111.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-113.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -616,9 +616,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -628,20 +632,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -753,6 +758,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -781,7 +809,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -841,6 +869,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -850,6 +879,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -862,6 +892,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -885,11 +916,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -621,6 +620,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-112.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-114.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -631,9 +631,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -643,20 +647,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -768,6 +773,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -796,7 +824,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -856,6 +884,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -865,6 +894,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -877,6 +907,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -900,11 +931,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -612,6 +611,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-113.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-115.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -622,9 +622,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -634,20 +638,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -759,6 +764,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -787,7 +815,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -847,6 +875,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -856,6 +885,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -868,6 +898,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -891,11 +922,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -621,6 +620,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-114.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-116.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -631,9 +631,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -643,20 +647,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -768,6 +773,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -796,7 +824,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -856,6 +884,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -865,6 +894,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -877,6 +907,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -900,11 +931,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -678,6 +677,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-115.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-117.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -688,9 +688,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -700,20 +704,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -825,6 +830,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -853,7 +881,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -913,6 +941,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -922,6 +951,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -934,6 +964,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -957,11 +988,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,6 +681,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-116.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-118.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -692,9 +692,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -704,20 +708,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -829,6 +834,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -857,7 +885,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -917,6 +945,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -926,6 +955,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -938,6 +968,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -961,11 +992,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -648,6 +647,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-117.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-119.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -658,9 +658,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -670,20 +674,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -795,6 +800,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -823,7 +851,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -883,6 +911,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -892,6 +921,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -904,6 +934,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -927,11 +958,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -645,6 +644,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-118.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-120.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -655,9 +655,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -667,20 +671,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -792,6 +797,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -820,7 +848,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -880,6 +908,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -889,6 +918,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -901,6 +931,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -924,11 +955,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -610,6 +609,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-11.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-13.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -620,9 +620,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -632,20 +636,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -738,6 +743,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -766,7 +794,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -826,6 +854,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -835,6 +864,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -847,6 +877,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -870,11 +901,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -632,6 +631,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-119.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-121.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -642,9 +642,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -654,20 +658,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -779,6 +784,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -807,7 +835,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -867,6 +895,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -876,6 +905,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -888,6 +918,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -911,11 +942,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -665,6 +664,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-120.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-122.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -675,9 +675,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -687,20 +691,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -812,6 +817,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -840,7 +868,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -900,6 +928,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -909,6 +938,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,6 +951,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -944,11 +975,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,6 +666,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-121.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-123.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -677,9 +677,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -689,20 +693,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -814,6 +819,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -842,7 +870,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -902,6 +930,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -911,6 +940,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -923,6 +953,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -946,11 +977,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,6 +681,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-122.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-124.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -692,9 +692,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -704,20 +708,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -829,6 +834,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -857,7 +885,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -917,6 +945,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -926,6 +955,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -938,6 +968,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -961,11 +992,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -689,6 +688,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-123.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-125.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -699,9 +699,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -711,20 +715,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -836,6 +841,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -864,7 +892,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -924,6 +952,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -933,6 +962,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -945,6 +975,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -968,11 +999,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -676,6 +675,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-124.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-126.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -686,9 +686,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -698,20 +702,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -823,6 +828,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -851,7 +879,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -911,6 +939,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -920,6 +949,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -932,6 +962,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -955,11 +986,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -858,6 +857,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-125.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-127.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -868,9 +868,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -880,20 +884,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -1005,6 +1010,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -1033,7 +1061,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -1093,6 +1121,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -1102,6 +1131,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1114,6 +1144,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1137,11 +1168,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -687,6 +686,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-126.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-128.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -697,9 +697,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -709,20 +713,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -834,6 +839,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -862,7 +890,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -922,6 +950,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -931,6 +960,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -943,6 +973,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -966,11 +997,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -683,6 +682,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-127.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-129.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -693,9 +693,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -705,20 +709,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -830,6 +835,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -858,7 +886,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -918,6 +946,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -927,6 +956,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -939,6 +969,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -962,11 +993,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,6 +652,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-128.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-130.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -663,9 +663,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -675,20 +679,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -800,6 +805,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -828,7 +856,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -888,6 +916,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -897,6 +926,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,6 +939,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -932,11 +963,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -626,6 +625,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-12.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-14.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -636,9 +636,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -648,20 +652,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -755,6 +760,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -783,7 +811,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -843,6 +871,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -852,6 +881,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -864,6 +894,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -887,11 +918,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -653,6 +652,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-129.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-131.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -663,9 +663,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -675,20 +679,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -800,6 +805,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -828,7 +856,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -888,6 +916,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -897,6 +926,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,6 +939,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -932,11 +963,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -678,6 +677,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-130.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-132.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -688,9 +688,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -700,20 +704,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -825,6 +830,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -853,7 +881,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -913,6 +941,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -922,6 +951,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -934,6 +964,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -957,11 +988,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -678,6 +677,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-131.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-133.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -688,9 +688,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -700,20 +704,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -825,6 +830,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -853,7 +881,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -913,6 +941,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -922,6 +951,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -934,6 +964,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -957,11 +988,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -690,6 +689,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-132.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-134.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -700,9 +700,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -712,20 +716,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -837,6 +842,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -865,7 +893,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -925,6 +953,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -934,6 +963,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -946,6 +976,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -969,11 +1000,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -655,6 +654,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-133.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-135.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -665,9 +665,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -677,20 +681,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -802,6 +807,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -830,7 +858,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -890,6 +918,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -899,6 +928,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -911,6 +941,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -934,11 +965,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -661,6 +660,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-134.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-136.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -671,9 +671,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -683,20 +687,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -808,6 +813,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -836,7 +864,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -896,6 +924,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -905,6 +934,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,6 +947,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -940,11 +971,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -642,6 +641,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-135.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-137.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -652,9 +652,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -664,20 +668,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -789,6 +794,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -817,7 +845,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -877,6 +905,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -886,6 +915,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -898,6 +928,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -921,11 +952,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,6 +665,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-136.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-138.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -676,9 +676,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -688,20 +692,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -813,6 +818,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -841,7 +869,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -901,6 +929,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -910,6 +939,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -922,6 +952,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -945,11 +976,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,6 +665,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-137.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-139.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -676,9 +676,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -688,20 +692,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -813,6 +818,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -841,7 +869,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -901,6 +929,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -910,6 +939,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -922,6 +952,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -945,11 +976,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -684,6 +683,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-138.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-140.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -694,9 +694,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -706,20 +710,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -831,6 +836,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -859,7 +887,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -919,6 +947,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -928,6 +957,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -940,6 +970,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -963,11 +994,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -636,6 +635,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-13.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-15.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -646,9 +646,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -658,20 +662,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -766,6 +771,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -794,7 +822,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -854,6 +882,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -863,6 +892,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -875,6 +905,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -898,11 +929,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -665,6 +664,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-139.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-141.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -675,9 +675,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -687,20 +691,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -812,6 +817,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -840,7 +868,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -900,6 +928,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -909,6 +938,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,6 +951,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -944,11 +975,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,6 +648,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-140.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-142.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -659,9 +659,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -671,20 +675,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -796,6 +801,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -824,7 +852,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -884,6 +912,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -893,6 +922,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -905,6 +935,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -928,11 +959,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,6 +658,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-141.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-143.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -669,9 +669,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -681,20 +685,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -806,6 +811,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -834,7 +862,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -894,6 +922,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -903,6 +932,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -915,6 +945,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -938,11 +969,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,6 +653,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-142.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-144.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -664,9 +664,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -676,20 +680,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -801,6 +806,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -829,7 +857,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -889,6 +917,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -898,6 +927,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -910,6 +940,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -933,11 +964,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -643,6 +642,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-143.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-145.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -653,9 +653,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -665,20 +669,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -790,6 +795,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -818,7 +846,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -878,6 +906,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -887,6 +916,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -899,6 +929,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -922,11 +953,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -635,6 +634,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-144.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-146.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -645,9 +645,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -657,20 +661,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -782,6 +787,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -810,7 +838,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -870,6 +898,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -879,6 +908,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -891,6 +921,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -914,11 +945,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -643,6 +642,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-145.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-147.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -653,9 +653,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -665,20 +669,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -790,6 +795,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -818,7 +846,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -878,6 +906,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -887,6 +916,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -899,6 +929,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -922,11 +953,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -693,6 +692,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-146.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-148.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -703,9 +703,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -715,20 +719,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -840,6 +845,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -868,7 +896,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -928,6 +956,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -937,6 +966,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,6 +979,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -972,11 +1003,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,6 +665,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-147.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-149.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -676,9 +676,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -688,20 +692,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -813,6 +818,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -841,7 +869,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -901,6 +929,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -910,6 +939,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -922,6 +952,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -945,11 +976,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -647,6 +646,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-148.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-150.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -657,9 +657,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -669,20 +673,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -793,6 +798,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -821,7 +849,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -881,6 +909,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -890,6 +919,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -902,6 +932,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -925,11 +956,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -704,6 +703,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-14.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-16.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -714,9 +714,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -726,20 +730,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -835,6 +840,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -863,7 +891,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -923,6 +951,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -932,6 +961,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -944,6 +974,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -967,11 +998,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -644,6 +643,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-149.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-151.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -654,9 +654,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -666,20 +670,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -789,6 +794,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -817,7 +845,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -877,6 +905,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -886,6 +915,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -898,6 +928,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -921,11 +952,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -651,6 +650,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-150.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-152.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -661,9 +661,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -673,20 +677,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -795,6 +800,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -823,7 +851,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -883,6 +911,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -892,6 +921,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -904,6 +934,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -927,11 +958,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -629,6 +628,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-151.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-153.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -639,9 +639,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -651,20 +655,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -772,6 +777,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -800,7 +828,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -860,6 +888,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -869,6 +898,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -881,6 +911,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -904,11 +935,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,6 +679,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-152.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-154.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -690,9 +690,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -702,20 +706,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -822,6 +827,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -850,7 +878,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -910,6 +938,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -919,6 +948,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -931,6 +961,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -954,11 +985,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -648,6 +647,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-153.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-155.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -658,9 +658,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -670,20 +674,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -789,6 +794,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -817,7 +845,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -877,6 +905,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -886,6 +915,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -898,6 +928,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -921,11 +952,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -691,6 +690,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-154.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-156.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -701,9 +701,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -713,20 +717,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -831,6 +836,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -859,7 +887,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -919,6 +947,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -928,6 +957,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -940,6 +970,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -963,11 +994,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -672,6 +671,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-155.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-157.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -682,9 +682,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -694,20 +698,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -811,6 +816,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -839,7 +867,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -899,6 +927,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -908,6 +937,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -920,6 +950,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -943,11 +974,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -659,6 +658,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-156.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-158.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -669,9 +669,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -681,20 +685,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -797,6 +802,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -825,7 +853,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -885,6 +913,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -894,6 +923,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -906,6 +936,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -929,11 +960,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,6 +681,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-157.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-159.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -692,9 +692,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -704,20 +708,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -819,6 +824,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -847,7 +875,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -907,6 +935,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -916,6 +945,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -928,6 +958,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -951,11 +982,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -660,6 +659,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-158.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-160.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -670,9 +670,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -682,20 +686,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -796,6 +801,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -824,7 +852,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -884,6 +912,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -893,6 +922,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -905,6 +935,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -928,11 +959,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -736,6 +735,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-15.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-17.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -746,9 +746,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -758,20 +762,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -868,6 +873,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -896,7 +924,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -956,6 +984,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -965,6 +994,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -977,6 +1007,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1000,11 +1031,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -657,6 +656,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-159.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-161.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -667,9 +667,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -679,20 +683,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -792,6 +797,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -820,7 +848,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -880,6 +908,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -889,6 +918,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -901,6 +931,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -924,11 +955,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -674,6 +673,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-160.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-162.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -684,9 +684,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -696,20 +700,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -808,6 +813,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -836,7 +864,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -896,6 +924,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -905,6 +934,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,6 +947,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -940,11 +971,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -664,6 +663,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-161.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-163.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -674,9 +674,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -686,20 +690,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -797,6 +802,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -825,7 +853,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -885,6 +913,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -894,6 +923,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -906,6 +936,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -929,11 +960,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -690,6 +689,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-162.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-164.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -700,9 +700,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -712,20 +716,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -822,6 +827,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -850,7 +878,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -910,6 +938,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -919,6 +948,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -931,6 +961,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -954,11 +985,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -706,6 +705,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-163.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-165.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -716,9 +716,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -728,20 +732,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -837,6 +842,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -865,7 +893,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -925,6 +953,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -934,6 +963,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -946,6 +976,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -969,11 +1000,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -740,6 +739,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-164.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-166.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -750,9 +750,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -762,20 +766,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -870,6 +875,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -898,7 +926,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -958,6 +986,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -967,6 +996,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -979,6 +1009,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1002,11 +1033,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -711,6 +710,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-165.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-167.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -721,9 +721,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -733,20 +737,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -840,6 +845,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -868,7 +896,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -928,6 +956,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -937,6 +966,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -949,6 +979,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -972,11 +1003,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,6 +679,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-166.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-168.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -690,9 +690,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -702,20 +706,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -808,6 +813,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -836,7 +864,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -896,6 +924,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -905,6 +934,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,6 +947,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -940,11 +971,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -682,6 +681,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-167.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-169.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -692,9 +692,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -704,20 +708,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -809,6 +814,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -837,7 +865,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -897,6 +925,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -906,6 +935,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,6 +948,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -941,11 +972,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -685,6 +684,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-168.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-170.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -695,9 +695,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -707,20 +711,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -811,6 +816,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -839,7 +867,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -899,6 +927,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -908,6 +937,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -920,6 +950,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -943,11 +974,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,6 +720,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-16.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-18.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -731,9 +731,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -743,20 +747,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -854,6 +859,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -882,7 +910,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -942,6 +970,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -951,6 +980,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -963,6 +993,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -986,11 +1017,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -686,6 +685,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-169.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-171.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -696,9 +696,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -708,20 +712,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -811,6 +816,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -839,7 +867,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -899,6 +927,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -908,6 +937,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -920,6 +950,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -943,11 +974,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -836,6 +835,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-170.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-172.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -846,9 +846,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -858,20 +862,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -960,6 +965,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -988,7 +1016,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -1048,6 +1076,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -1057,6 +1086,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1069,6 +1099,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1092,11 +1123,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -761,6 +760,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-171.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-173.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -771,9 +771,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -783,20 +787,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -884,6 +889,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -912,7 +940,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -972,6 +1000,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -981,6 +1010,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -993,6 +1023,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1016,11 +1047,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,6 +781,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-172.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-174.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -792,9 +792,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -804,20 +808,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -904,6 +909,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -932,7 +960,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -992,6 +1020,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -1001,6 +1030,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1013,6 +1043,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1036,11 +1067,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -777,6 +776,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-173.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-175.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -787,9 +787,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -799,20 +803,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -898,6 +903,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -926,7 +954,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -986,6 +1014,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -995,6 +1024,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1007,6 +1037,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1030,11 +1061,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -770,6 +769,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-174.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-176.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -780,9 +780,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -792,20 +796,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -890,6 +895,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -918,7 +946,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -978,6 +1006,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -987,6 +1016,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -999,6 +1029,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1022,11 +1053,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -769,6 +768,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-175.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-177.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -779,9 +779,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -791,20 +795,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -888,6 +893,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -916,7 +944,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -976,6 +1004,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -985,6 +1014,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -997,6 +1027,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1020,11 +1051,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -758,6 +757,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-176.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-178.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -768,9 +768,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -780,20 +784,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -876,6 +881,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -904,7 +932,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -964,6 +992,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -973,6 +1002,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -985,6 +1015,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1008,11 +1039,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -824,6 +823,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-177.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-179.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -834,9 +834,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -846,20 +850,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -941,6 +946,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -969,7 +997,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -1029,6 +1057,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -1038,6 +1067,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1050,6 +1080,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -1073,11 +1104,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,6 +674,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-17.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-19.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -685,9 +685,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -697,20 +701,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -809,6 +814,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -837,7 +865,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -897,6 +925,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -906,6 +935,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,6 +948,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -941,11 +972,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,6 +687,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-18.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-20.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -698,9 +698,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -710,20 +714,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -823,6 +828,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -851,7 +879,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -911,6 +939,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -920,6 +949,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -932,6 +962,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -955,11 +986,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -656,6 +655,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-19.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-21.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -666,9 +666,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -678,20 +682,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -792,6 +797,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -820,7 +848,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -880,6 +908,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -889,6 +918,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -901,6 +931,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -924,11 +955,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -660,6 +659,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-20.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-22.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -670,9 +670,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -682,20 +686,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -797,6 +802,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -825,7 +853,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -885,6 +913,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -894,6 +923,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -906,6 +936,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -929,11 +960,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -667,6 +666,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-21.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-23.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -677,9 +677,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -689,20 +693,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -805,6 +810,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -833,7 +861,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -893,6 +921,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -902,6 +931,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -914,6 +944,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -937,11 +968,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -686,6 +685,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-22.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-24.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -696,9 +696,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -708,20 +712,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -825,6 +830,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -853,7 +881,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -913,6 +941,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -922,6 +951,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -934,6 +964,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -957,11 +988,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -717,6 +716,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-23.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-25.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -727,9 +727,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -739,20 +743,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -857,6 +862,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -885,7 +913,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -945,6 +973,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -954,6 +983,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -966,6 +996,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -989,11 +1020,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -634,6 +633,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-24.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-26.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -644,9 +644,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -656,20 +660,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -775,6 +780,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -803,7 +831,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -863,6 +891,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -872,6 +901,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -884,6 +914,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -907,11 +938,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -712,6 +711,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-25.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-27.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -722,9 +722,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -734,20 +738,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -854,6 +859,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -882,7 +910,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -942,6 +970,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -951,6 +980,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -963,6 +993,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -986,11 +1017,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -645,6 +644,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-26.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-28.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -655,9 +655,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -667,20 +671,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -788,6 +793,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -816,7 +844,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -876,6 +904,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -885,6 +914,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -897,6 +927,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -920,11 +951,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -672,6 +671,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-27.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-29.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -682,9 +682,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -694,20 +698,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -816,6 +821,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -844,7 +872,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -904,6 +932,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -913,6 +942,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -925,6 +955,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -948,11 +979,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
@ -188,6 +188,11 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* 右侧滚动按钮 */
|
||||
.scroll-buttons {
|
||||
position: fixed;
|
||||
@ -219,41 +224,59 @@
|
||||
background: var(--btn-hover);
|
||||
}
|
||||
|
||||
/* TTS控制面板 */
|
||||
.tts-panel {
|
||||
/* TTS弹出面板 */
|
||||
.tts-overlay {
|
||||
position: fixed;
|
||||
left: 20px;
|
||||
bottom: 90px;
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 1002;
|
||||
background: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
.tts-panel {
|
||||
background: rgba(30,30,50,0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 15px;
|
||||
min-width: 180px;
|
||||
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: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
.tts-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
@ -261,33 +284,58 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
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 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tts-progress-bar {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
height: 6px;
|
||||
background: var(--btn-bg);
|
||||
border-radius: 2px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tts-progress-fill {
|
||||
@ -298,94 +346,36 @@
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
min-width: 80px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tts-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: var(--text-secondary);
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 4px 8px;
|
||||
padding: 8px 16px;
|
||||
background: var(--btn-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-primary);
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
font-family: 'Noto Sans SC', sans-serif;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.tts-panel {
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: auto;
|
||||
top: 60px;
|
||||
min-width: auto;
|
||||
padding: 12px;
|
||||
width: auto;
|
||||
z-index: 1003;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-speed-select {
|
||||
padding: 3px 6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.tts-time {
|
||||
font-size: 11px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
/* 手机端隐藏滚动按钮,给TTS腾空间 */
|
||||
.scroll-buttons {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.tts-panel {
|
||||
top: 55px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.tts-controls {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 侧边栏 - 标题固定,内容滚动 */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@ -496,22 +486,31 @@
|
||||
}
|
||||
|
||||
.fixed-nav-content {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 10px 15px;
|
||||
padding: 10px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scroll-buttons {
|
||||
right: 10px;
|
||||
bottom: 80px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scroll-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
.tts-panel {
|
||||
padding: 20px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.tts-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tts-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -641,6 +640,7 @@
|
||||
<div class="fixed-nav-content">
|
||||
<a href="chapter-28.html" class="nav-btn ">上一章</a>
|
||||
<a href="../chapters.html" class="nav-btn">目录</a>
|
||||
<button class="nav-btn" id="ttsToggleBtn">朗读</button>
|
||||
<a href="chapter-30.html" class="nav-btn ">下一章</a>
|
||||
</div>
|
||||
</nav>
|
||||
@ -651,9 +651,13 @@
|
||||
<button class="scroll-btn" id="scrollBottom" title="回到底部">底</button>
|
||||
</div>
|
||||
|
||||
<!-- TTS语音朗读面板 -->
|
||||
<div class="tts-panel" id="ttsPanel">
|
||||
<div class="tts-title">语音朗读</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>
|
||||
@ -663,20 +667,21 @@
|
||||
<div class="tts-progress-bar">
|
||||
<div class="tts-progress-fill" id="ttsProgressFill"></div>
|
||||
</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0</div>
|
||||
<div class="tts-time" id="ttsTime">0 / 0 句</div>
|
||||
</div>
|
||||
<div class="tts-speed">
|
||||
<span class="tts-speed-label">速度:</span>
|
||||
<span class="tts-speed-label">朗读速度:</span>
|
||||
<select class="tts-speed-select" id="ttsSpeed">
|
||||
<option value="0.5">慢速</option>
|
||||
<option value="0.75">较慢</option>
|
||||
<option value="1" selected>正常</option>
|
||||
<option value="1.25">较快</option>
|
||||
<option value="1.5">快速</option>
|
||||
<option value="2">极速</option>
|
||||
<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">
|
||||
@ -786,6 +791,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
// ========== 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;
|
||||
@ -814,7 +842,7 @@
|
||||
function updateTTSProgress() {
|
||||
const total = ttsSentences.length;
|
||||
const current = ttsCurrentIndex;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total}`;
|
||||
document.getElementById('ttsTime').textContent = `${current} / ${total} 句`;
|
||||
const percent = total > 0 ? (current / total * 100) : 0;
|
||||
document.getElementById('ttsProgressFill').style.width = `${percent}%`;
|
||||
}
|
||||
@ -874,6 +902,7 @@
|
||||
|
||||
document.getElementById('ttsPlay').classList.add('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.add('active');
|
||||
}
|
||||
|
||||
// 暂停
|
||||
@ -883,6 +912,7 @@
|
||||
ttsIsPaused = true;
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.add('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
@ -895,6 +925,7 @@
|
||||
updateTTSProgress();
|
||||
document.getElementById('ttsPlay').classList.remove('active');
|
||||
document.getElementById('ttsPause').classList.remove('active');
|
||||
ttsToggleBtn.classList.remove('active');
|
||||
}
|
||||
|
||||
// 设置速度
|
||||
@ -918,11 +949,6 @@
|
||||
speechSynthesis.onvoiceschanged = initTTS;
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
window.addEventListener('load', () => {
|
||||
initTTS();
|
||||
});
|
||||
|
||||
// 页面离开时停止播放
|
||||
window.addEventListener('beforeunload', stopTTS);
|
||||
</script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user