--- abbrlink: 1126783921 categories: - 往昔 date: "2025-07-04 10:46:53" tags: - 翻译 - JavaScript title: 使用Javascript TSS和Highlights构建一个文本阅读器 --- > 原文:https://jsdev.space/tts-sentence-reader/ > > 翻译:Verdant ![](https://jsdev.space/.netlify/images?url=_astro%2Ftts-sentence-reader.hV9whx3I.png&w=800&h=800) 在这篇文章中,我们将构建一个简单的Web工具来探究**Text-toSpeech(TTS)**在JavaScript中是如何工作的。我们也将深入研究**句子级高亮**的工作逻辑。这两项功能经常结合在一起使用,以走到浏览器中打造无障碍的动态阅读体验。 步骤: 1. 学习在浏览器中,TTS是如何工作的 2. 探究动态高亮句子的实现方法 3. 用HTML, CSS, JavaScript构建一个小工具([Demo & Code](https://codepen.io/jsdevspace/pen/YPXRRjO)) ## 📢 浏览器中的TTS概述 JavaScript提供一个内置的API:[`SpeechSynthesis`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis),它允许我们使用系统中的可用嗓音去大声朗读问文字。 ### 核心对象: - `speechSynthesis` — 控制播放、暂停、恢复、停止 - `SpeechSynthesisUtterance` — 作为TTS引擎的待播报文本 ### ✨示例: ```js const msg = new SpeechSynthesisUtterance("Hello, world!"); window.speechSynthesis.speak(msg); ``` ### ⚙️ 添加嗓音和配置 ```js const utter = new SpeechSynthesisUtterance("This is a test"); const voices = window.speechSynthesis.getVoices(); utter.voice = voices.find(v => v.lang === 'en-US'); utter.rate = 1.2; utter.pitch = 1; window.speechSynthesis.speak(utter); ``` 你也可以追踪朗读的开始和结束: ```js utter.onstart = () => console.log('Started speaking'); utter.onend = () => console.log('Finished speaking'); ``` ## ✍️ 句子高亮 展示给用户哪一个句子正在阅读,我们需要用CSS和JavaScript来高亮文本。 ### 示例 HTML: ```html

First sentence. Second sentence.

``` ### 处理高亮的CSS: ```css .sentence.active { background-color: yellow; font-weight: bold; } ``` ### JavaScript高亮逻辑: ```js function highlight(index) { document.querySelectorAll('.sentence').forEach((el, i) => { el.classList.toggle('active', i === index); }); } ``` # 🚀项目: 使用TSS和高亮的阅读器 我们的程序将要: - 逐句朗读文本 - 高亮朗读中的文本 - 提供播放、暂停、恢复、停止 - 让用户能选择嗓音 ## 📄 HTML结构 ```html Interactive TTS Article Reader

Read Along TTS Demo

Learning to code is a never-ending journey. Technologies evolve rapidly, requiring constant adaptation. JavaScript, HTML, and CSS are essential tools for web development. Frameworks like React and Vue enhance front-end capabilities. Back-end skills with Node.js extend JavaScript to the server.
0/0
``` ## 🎨 CSS 样式 ```css body { font-family: sans-serif; margin: 0; padding: 2rem; background: #f0f4f8; color: #333; } h1 { text-align: center; margin-bottom: 2rem; } .toolbar { display: flex; justify-content: center; flex-wrap: wrap; gap: 1rem; margin-bottom: 2rem; } .toolbar button, .toolbar select { padding: 0.6rem 1.2rem; border-radius: 4px; border: none; font-size: 1rem; } .toolbar button { background: #0077cc; color: white; cursor: pointer; } .toolbar button:disabled { background: #ccc; cursor: not-allowed; } .text-block { background: white; padding: 1.5rem; border-radius: 6px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); } .line { display: block; margin-bottom: 1rem; cursor: pointer; transition: background 0.3s; } .line:hover { background: #f9f9f9; } .line.active { background: #fff3cd; font-weight: bold; } .progress { text-align: center; margin-top: 1rem; } .bar { height: 8px; width: 100%; background: #eee; border-radius: 4px; overflow: hidden; } .bar-fill { height: 100%; width: 0; background: linear-gradient(to right, #0077cc, #005fa3); transition: width 0.3s; } ``` ## 💡 JavaScript逻辑 ```js const lines = document.querySelectorAll('.line'); const playBtn = document.getElementById('start'); const pauseBtn = document.getElementById('pause'); const resumeBtn = document.getElementById('resume'); const stopBtn = document.getElementById('stop'); const resetBtn = document.getElementById('reset'); const voiceSelect = document.getElementById('voices'); const progressText = document.getElementById('progressText'); const progressBar = document.getElementById('bar'); const synth = window.speechSynthesis; let voices = []; let currentIndex = 0; let currentUtterance = null; let isPaused = false; function populateVoices() { voices = synth.getVoices(); voiceSelect.innerHTML = ''; voices.forEach((voice, index) => { const opt = document.createElement('option'); opt.value = index; opt.textContent = `${voice.name} (${voice.lang})`; voiceSelect.appendChild(opt); }); } synth.onvoiceschanged = populateVoices; populateVoices(); function updateUI() { progressText.textContent = `${currentIndex + 1}/${lines.length}`; progressBar.style.width = `${((currentIndex + 1) / lines.length) * 100}%`; } function highlightLine(index) { lines.forEach((line, i) => { line.classList.toggle('active', i === index); }); lines[index]?.scrollIntoView({ behavior: 'smooth', block: 'center' }); } function speakLine(index) { if (index >= lines.length) return; const text = lines[index].textContent; const utter = new SpeechSynthesisUtterance(text); const selected = voiceSelect.value; if (voices[selected]) utter.voice = voices[selected]; utter.rate = 1; utter.onstart = () => { highlightLine(index); playBtn.disabled = true; pauseBtn.disabled = false; resumeBtn.disabled = true; stopBtn.disabled = false; }; utter.onend = () => { if (!isPaused) { currentIndex++; if (currentIndex < lines.length) { speakLine(currentIndex); } else { resetControls(); } } }; currentUtterance = utter; synth.speak(utter); updateUI(); } function resetControls() { playBtn.disabled = false; pauseBtn.disabled = true; resumeBtn.disabled = true; stopBtn.disabled = true; } playBtn.onclick = () => { currentIndex = 0; speakLine(currentIndex); }; pauseBtn.onclick = () => { synth.pause(); isPaused = true; pauseBtn.disabled = true; resumeBtn.disabled = false; }; resumeBtn.onclick = () => { synth.resume(); isPaused = false; pauseBtn.disabled = false; resumeBtn.disabled = true; }; stopBtn.onclick = () => { synth.cancel(); resetControls(); }; resetBtn.onclick = () => { synth.cancel(); currentIndex = 0; highlightLine(currentIndex); updateUI(); }; lines.forEach((line, index) => { line.addEventListener('click', () => { synth.cancel(); currentIndex = index; speakLine(currentIndex); }); }); updateUI(); ``` ## ✅ 它们如何工作 - 每一个句子都是`` - 我们迭代句子并使用`SpeechSynthesisUtterance`大声朗读 - 在朗读过程中,高亮正确的文本并滚动 - 一个句子结束后,自动朗读下一个句子 ## 🔚 尾声 阅读了本文,你理解了: - 浏览器TTS的运行原理 - 如何实现动态高亮文本 - 如何从零构建一个功能齐全的阅读界面 你可以扩展项目功能: - 在`localStorage`保存阅读进度 - 添加进度条 - 加载外部文章或用户输入