SpeechSynthesis无法完整朗读文本,求连续朗读解决方案
浏览器原生TTS分块朗读中断问题修复
我使用浏览器原生TTS朗读返回内容时,发现它无法朗读完整句子,于是将文本拆分为块,但即使分块,仍会在190-200字符处停止,无法朗读剩余内容。需求是实现连续朗读,无论是否分块,都要读完全部回答内容。当前有一个带音频图标的按钮,通过onClick={() => handleSpeak(message.content)}调用朗读函数,原代码如下:
useEffect(() => { if (messages.length > 0) { setAllMessages(prevMessages => { const newMessages = messages.filter( newMessage => !prevMessages.some(prevMessage => prevMessage.content === newMessage.content) ); return [...prevMessages, ...newMessages]; }); } }, [messages]); const cleanText = (html) => { const div = document.createElement('div'); div.innerHTML = html; return div.textContent || div.innerText || ''; }; const speakInChunks = (text) => { const maxChunkLength = 200; // 每个分块的最大长度 const chunks = []; // 将文本拆分为最多200字符的块,不拆分单词 while (text.length > 0) { let chunk = text.slice(0, maxChunkLength); const lastSpaceIndex = chunk.lastIndexOf(' '); if (lastSpaceIndex > 0 && chunk.length === maxChunkLength) { chunk = chunk.slice(0, lastSpaceIndex); // 避免拆分单词 } chunks.push(chunk); text = text.slice(chunk.length).trim(); // 移除已处理的部分 } console.log("已创建分块:", chunks); let currentChunk = 0; // 朗读每个分块的函数 const speakChunk = () => { if (currentChunk < chunks.length) { const utterance = new SpeechSynthesisUtterance(chunks[currentChunk]); utterance.lang = 'pt-BR'; // 设置语言为葡萄牙语 utterance.onend = () => { console.log(`正在朗读分块 ${currentChunk + 1}: ${chunks[currentChunk]}`); currentChunk++; if (currentChunk < chunks.length) { window.speechSynthesis.pause(); setTimeout(() => { window.speechSynthesis.resume(); speakChunk(); }, 500); // 分块间暂停500ms } }; window.speechSynthesis.speak(utterance); // 朗读当前分块 } }; window.speechSynthesis.cancel(); // 取消之前的朗读 speakChunk(); // 开始朗读流程 }; const handleSpeak = (messageContent) => { const cleanedText = cleanText(messageContent); speakInChunks(cleanedText); // 分块朗读文本 };
问题分析与修复方案
原代码中多余的pause()和resume()操作会干扰TTS的正常调度,部分浏览器会因此中断后续朗读。另外,需要确保前一个朗读任务完全结束后再启动下一个,避免队列冲突。
修改后的代码如下:
useEffect(() => { if (messages.length > 0) { setAllMessages(prevMessages => { const newMessages = messages.filter( newMessage => !prevMessages.some(prevMessage => prevMessage.content === newMessage.content) ); return [...prevMessages, ...newMessages]; }); } }, [messages]); const cleanText = (html) => { const div = document.createElement('div'); div.innerHTML = html; return div.textContent || div.innerText || ''; }; const speakInChunks = (text) => { const maxChunkLength = 200; const chunks = []; // 优化分块逻辑,确保无空块 while (text.length > 0) { let chunk = text.slice(0, maxChunkLength); const lastSpaceIndex = chunk.lastIndexOf(' '); if (lastSpaceIndex > 0 && chunk.length === maxChunkLength) { chunk = chunk.slice(0, lastSpaceIndex); } // 跳过空块 if (chunk.trim()) { chunks.push(chunk.trim()); } text = text.slice(chunk.length).trim(); } console.log("已创建分块:", chunks); let currentChunk = 0; const speakChunk = () => { if (currentChunk >= chunks.length) return; // 检查TTS是否处于可用状态 if (window.speechSynthesis.speaking) { window.speechSynthesis.cancel(); } const utterance = new SpeechSynthesisUtterance(chunks[currentChunk]); utterance.lang = 'pt-BR'; utterance.onend = () => { console.log(`完成朗读分块 ${currentChunk + 1}: ${chunks[currentChunk]}`); currentChunk++; // 直接触发下一个分块,无需暂停恢复 speakChunk(); }; // 处理可能的错误 utterance.onerror = (event) => { console.error(`朗读分块 ${currentChunk + 1} 出错:`, event.error); currentChunk++; speakChunk(); }; window.speechSynthesis.speak(utterance); }; // 先清空之前的朗读任务 window.speechSynthesis.cancel(); speakChunk(); }; const handleSpeak = (messageContent) => { const cleanedText = cleanText(messageContent); if (cleanedText.trim()) { speakInChunks(cleanedText); } };
关键修改点
- 移除了分块间的
pause()和resume()操作,直接在onend回调中启动下一个朗读任务 - 增加了空块过滤,避免朗读空白内容
- 添加了
onerror回调,处理朗读出错的情况,确保流程继续 - 增加了TTS状态检查,避免重复调度冲突
内容的提问来源于stack exchange,提问作者Rayssa
相关产品推荐
相关产品推荐

