JS純前端實(shí)現(xiàn)瀏覽器語音播報、朗讀功能的完整代碼
一、朗讀單條文本:
① 語音自選參數(shù),按鈕控制語音:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>文本轉(zhuǎn)語音播報</title> <style> body { background: #f0f0f0; text-align: center; } .select { display: flex; justify-content: center; align-items: center; } button { font-size: 18px; } #status { margin: 20px; color: yellowgreen; } </style> </head> <body> <div style="color: rgb(248, 74, 103);">文本轉(zhuǎn)語音播報:此功能依賴瀏覽器支持Web Speech API,目前主流瀏覽器(Chrome、Edge等)均已支持</div> <div> <h2>輸入要朗讀的文本:</h2> <textarea id="textToSpeak" placeholder="請輸入需要朗讀的文本內(nèi)容..." rows="20" cols="100"></textarea> </div> <div class="select"> <h3>選擇語音:</h3> <select id="voiceSelect"> <option value="">加載中...</option> </select> </div> <div class="select"> <h3>語速:</h3> <input type="range" id="rate" min="0.5" max="2" step="0.1" value="1"> </div> <div class="select"> <h3>音調(diào):</h3> <input type="range" id="pitch" min="0.5" max="2" step="0.1" value="1"> </div> <div class="select"> <h3>音量:</h3> <input type="range" id="volume" min="0" max="1" step="0.1" value="1"> </div> <!-- 控制按鈕區(qū)域 --> <div> <button id="startBtn">開始朗讀</button> <button id="pauseBtn" disabled>暫停</button> <button id="resumeBtn" disabled>繼續(xù)</button> <button id="cancelBtn" disabled>停止</button> </div> <!-- 狀態(tài)顯示區(qū)域 --> <div id="status"></div> </body> <script> // 檢查瀏覽器是否支持Web Speech API if ('speechSynthesis' in window) { const synth = window.speechSynthesis; let voices = []; // DOM元素 const textInput = document.getElementById('textToSpeak'); const voiceSelect = document.getElementById('voiceSelect'); const rateInput = document.getElementById('rate'); const pitchInput = document.getElementById('pitch'); const volumeInput = document.getElementById('volume'); const rateValue = document.getElementById('rateValue'); const pitchValue = document.getElementById('pitchValue'); const volumeValue = document.getElementById('volumeValue'); const startBtn = document.getElementById('startBtn'); const pauseBtn = document.getElementById('pauseBtn'); const resumeBtn = document.getElementById('resumeBtn'); const cancelBtn = document.getElementById('cancelBtn'); const statusDisplay = document.getElementById('status'); // 獲取可用語音列表 function loadVoices() { voices = synth.getVoices(); voiceSelect.innerHTML = ''; // 篩選中文語音優(yōu)先顯示 const chineseVoices = voices.filter(voice => voice.lang.includes('zh') || voice.name.includes('Chinese') ); const otherVoices = voices.filter(voice => !voice.lang.includes('zh') && !voice.name.includes('Chinese') ); // 添加中文語音 chineseVoices.forEach(voice => { const option = document.createElement('option'); option.textContent = `${voice.name} (${voice.lang})`; option.value = voice.name; voiceSelect.appendChild(option); }); // 如果有中文語音,添加分隔線 if (chineseVoices.length > 0 && otherVoices.length > 0) { const separator = document.createElement('option'); separator.textContent = '────────── 其他語言 ──────────'; separator.disabled = true; voiceSelect.appendChild(separator); } // 添加其他語音 otherVoices.forEach(voice => { const option = document.createElement('option'); option.textContent = `${voice.name} (${voice.lang})`; option.value = voice.name; voiceSelect.appendChild(option); }); // 默認(rèn)選擇第一個中文語音 if (chineseVoices.length > 0) { voiceSelect.value = chineseVoices[0].name; } else if (voices.length > 0) { voiceSelect.value = voices[0].name; } } // 初始化時加載語音,某些瀏覽器需要觸發(fā)一次語音合成才能加載語音列表 loadVoices(); if (speechSynthesis.onvoiceschanged !== undefined) { speechSynthesis.onvoiceschanged = loadVoices; } // 顯示當(dāng)前速率、音調(diào)、音量值 rateInput.addEventListener('input', () => { rateValue.textContent = rateInput.value; }); pitchInput.addEventListener('input', () => { pitchValue.textContent = pitchInput.value; }); volumeInput.addEventListener('input', () => { volumeValue.textContent = volumeInput.value; }); // 開始朗讀 startBtn.addEventListener('click', () => { if (synth.speaking) { synth.cancel(); } const text = textInput.value.trim(); if (text) { statusDisplay.textContent = '正在朗讀...'; updateButtonStates(true); const utterThis = new SpeechSynthesisUtterance(text); // 設(shè)置語音 const selectedVoice = voices.find(voice => voice.name === voiceSelect.value); if (selectedVoice) { utterThis.voice = selectedVoice; } // 設(shè)置語音參數(shù) utterThis.rate = parseFloat(rateInput.value); utterThis.pitch = parseFloat(pitchInput.value); utterThis.volume = parseFloat(volumeInput.value); // 朗讀結(jié)束時的回調(diào) utterThis.onend = () => { statusDisplay.textContent = '朗讀完成'; updateButtonStates(false); }; // 朗讀出錯時的回調(diào) utterThis.onerror = (event) => { statusDisplay.textContent = `朗讀出錯: ${event.error}`; updateButtonStates(false); }; synth.speak(utterThis); } else { statusDisplay.textContent = '請輸入要朗讀的文本'; } }); // 暫停朗讀 pauseBtn.addEventListener('click', () => { if (synth.speaking) { if (synth.paused) { // 如果已經(jīng)暫停,則繼續(xù) synth.resume(); statusDisplay.textContent = '繼續(xù)朗讀...'; pauseBtn.disabled = false; resumeBtn.disabled = true; } else { // 暫停朗讀 synth.pause(); statusDisplay.textContent = '已暫停'; pauseBtn.disabled = true; resumeBtn.disabled = false; } } }); // 繼續(xù)朗讀 resumeBtn.addEventListener('click', () => { if (synth.paused) { synth.resume(); statusDisplay.textContent = '繼續(xù)朗讀...'; pauseBtn.disabled = false; resumeBtn.disabled = true; } }); // 停止朗讀 cancelBtn.addEventListener('click', () => { synth.cancel(); statusDisplay.textContent = '已停止'; updateButtonStates(false); }); // 更新按鈕狀態(tài) function updateButtonStates(isSpeaking) { startBtn.disabled = isSpeaking; pauseBtn.disabled = !isSpeaking; cancelBtn.disabled = !isSpeaking; resumeBtn.disabled = true; } } else { // 瀏覽器不支持Web Speech API時的處理 document.getElementById('status').textContent = '抱歉,您的瀏覽器不支持語音合成功能,請使用Chrome、Edge等現(xiàn)代瀏覽器。'; // 禁用所有控制按鈕 const buttons = document.querySelectorAll('button'); buttons.forEach(button => { button.disabled = true; button.classList.add('opacity-50', 'cursor-not-allowed'); }); // 禁用選擇框 document.getElementById('voiceSelect').disabled = true; const sliders = document.querySelectorAll('input[type="range"]'); sliders.forEach(slider => { slider.disabled = true; }); } </script> </body> </html>
② 效果圖:
二、朗讀多條文本:
① 語音有默認(rèn)值:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>文本轉(zhuǎn)語音播報(后端數(shù)據(jù))</title> <style> body { background: #f0f0f0; text-align: center; padding: 20px; font-family: Arial, sans-serif; } .control-panel { margin: 20px 0; } button { font-size: 18px; padding: 8px 16px; margin: 0 5px; cursor: pointer; border: none; border-radius: 4px; background: #4CAF50; color: white; } button:disabled { background: #cccccc; cursor: not-allowed; } #status { margin: 20px auto; color: #333; padding: 10px; background: #e8f0fe; display: inline-block; min-width: 300px; border-radius: 4px; } .alarm-list { max-width: 600px; margin: 20px auto; text-align: left; background: white; padding: 15px; border-radius: 4px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); } .alarm-item { padding: 8px 0; border-bottom: 1px solid #eee; } .alarm-item:last-child { border-bottom: none; } .loading { color: #666; font-style: italic; } </style> </head> <body> <div style="color: rgb(248, 74, 103);">文本轉(zhuǎn)語音播報:此功能依賴瀏覽器支持Web Speech API,目前主流瀏覽器(Chrome、Edge等)均已支持</div> <!-- 報警信息列表預(yù)覽 --> <div class="alarm-list"> <h4>待朗讀報警信息:</h4> <div id="alarmContainer" class="loading">正在從后端獲取數(shù)據(jù)...</div> </div> <!-- 控制按鈕區(qū)域 --> <div class="control-panel"> <button id="fetchDataBtn">獲取后端數(shù)據(jù)</button> <button id="initVoiceBtn" disabled>初始化語音(必須先獲取數(shù)據(jù))</button> <button id="startBtn" disabled>開始批量朗讀</button> <button id="pauseBtn" disabled>暫停</button> <button id="resumeBtn" disabled>繼續(xù)</button> <button id="cancelBtn" disabled>停止</button> </div> <!-- 狀態(tài)顯示區(qū)域 --> <div id="status">請點(diǎn)擊"獲取后端數(shù)據(jù)"按鈕開始</div> </body> <script> // 檢查瀏覽器是否支持Web Speech API if ('speechSynthesis' in window) { const synth = window.speechSynthesis; let voices = []; let isVoiceReady = false; // 語音是否準(zhǔn)備就緒 let currentAlarmIndex = 0; // 當(dāng)前播放的報警索引 let alarmList = []; // 報警信息列表(后端返回的數(shù)據(jù)) let isPlaying = false; // 是否正在播放 // DOM元素 const fetchDataBtn = document.getElementById('fetchDataBtn'); const initVoiceBtn = document.getElementById('initVoiceBtn'); const startBtn = document.getElementById('startBtn'); const pauseBtn = document.getElementById('pauseBtn'); const resumeBtn = document.getElementById('resumeBtn'); const cancelBtn = document.getElementById('cancelBtn'); const statusDisplay = document.getElementById('status'); const alarmContainer = document.getElementById('alarmContainer'); // 模擬從后端獲取報警信息 fetchDataBtn.addEventListener('click', async () => { statusDisplay.textContent = '正在從后端獲取報警信息...'; fetchDataBtn.disabled = true; try { // 模擬API請求延遲 const response = await simulateBackendRequest(); if (response.success && response.data && response.data.length > 0) { alarmList = response.data; displayAlarmList(alarmList); statusDisplay.textContent = `成功獲取${alarmList.length}條報警信息,請初始化語音`; initVoiceBtn.disabled = false; // 啟用初始化語音按鈕 } else { statusDisplay.textContent = '后端未返回任何報警信息'; fetchDataBtn.disabled = false; } } catch (err) { statusDisplay.textContent = `獲取數(shù)據(jù)失?。?{err.message}`; fetchDataBtn.disabled = false; } }); // 模擬后端請求 function simulateBackendRequest() { return new Promise((resolve) => { // 模擬網(wǎng)絡(luò)延遲 setTimeout(() => { // 模擬后端返回的數(shù)據(jù) const mockData = { success: true, data: [ { "id": 1, "title": "空調(diào)異常警報", "text": "25樓北區(qū)空調(diào)參數(shù)異常:總有功功率實(shí)際值5.79kW,異常狀態(tài)累計15分0秒", "type": 1, "typeText": "輕微", "timestamp": "2023-10-15 09:23:45" }, { "id": 2, "title": "配電柜溫度過高", "text": "B棟配電室3號柜溫度超標(biāo):當(dāng)前溫度62°C(閾值55°C),持續(xù)8分30秒", "type": 2, "typeText": "嚴(yán)重", "timestamp": "2023-10-15 10:15:22" }, { "id": 3, "title": "水泵壓力異常", "text": "地下二層供水系統(tǒng)壓力異常:當(dāng)前壓力0.25MPa(正常范圍0.3-0.5MPa),持續(xù)12分鐘", "type": 1, "typeText": "輕微", "timestamp": "2023-10-15 11:07:18" }, { "id": 4, "title": "消防水箱水位不足", "text": "1號樓消防水箱水位過低:當(dāng)前水位1.2m(最低警戒線1.5m),請立即處理", "type": 3, "typeText": "緊急", "timestamp": "2023-10-15 13:45:03" } ] }; resolve(mockData); }, 1500); // 1.5秒延遲,模擬真實(shí)網(wǎng)絡(luò)請求 }); } // 在頁面上顯示報警信息列表 function displayAlarmList(alarms) { alarmContainer.innerHTML = ''; alarms.forEach((alarm, index) => { const item = document.createElement('div'); item.className = 'alarm-item'; item.innerHTML = `${index + 1}. ${alarm.text} <span style="color:#666;font-size:0.8em;">(${alarm.typeText})</span>`; alarmContainer.appendChild(item); }); } // 初始化語音引擎 initVoiceBtn.addEventListener('click', async () => { statusDisplay.textContent = '正在初始化語音引擎...'; initVoiceBtn.disabled = true; try { const loadedVoices = await getAvailableVoices(); // 篩選中文語音(確保有可用的中文語音) const chineseVoices = loadedVoices.filter(v => v.lang === 'zh-CN' || v.lang === 'zh' || v.name.includes('Chinese') || v.name.includes('微軟') || v.name.includes('慧濤') ); if (chineseVoices.length === 0) { statusDisplay.textContent = '未找到中文語音包,請先在系統(tǒng)中安裝中文語音(如Windows的“微軟慧濤”)'; initVoiceBtn.disabled = false; return; } // 語音初始化完成 isVoiceReady = true; startBtn.disabled = false; statusDisplay.textContent = `語音初始化成功!找到${chineseVoices.length}個中文語音包,點(diǎn)擊"開始批量朗讀"按鈕`; console.log('可用中文語音:', chineseVoices); } catch (err) { statusDisplay.textContent = `語音初始化失?。?{err.message}`; initVoiceBtn.disabled = false; } }); // 獲取語音列表(帶重試機(jī)制) function getAvailableVoices() { return new Promise((resolve, reject) => { // 最多重試3次 let attempts = 0; const checkVoices = () => { const voices = synth.getVoices(); if (voices.length > 0) { resolve(voices); } else if (attempts < 3) { attempts++; // 觸發(fā)一次空語音來激活引擎 const testUtter = new SpeechSynthesisUtterance(''); synth.speak(testUtter); setTimeout(() => { synth.cancel(); checkVoices(); }, 500); } else { reject(new Error('無法加載語音列表,請刷新頁面重試')); } }; // 監(jiān)聽語音變化事件 synth.onvoiceschanged = checkVoices; // 立即檢查一次 checkVoices(); }); } // 開始批量朗讀 startBtn.addEventListener('click', () => { if (!isVoiceReady || alarmList.length === 0) return; isPlaying = true; currentAlarmIndex = 0; // 從第一條開始 updateButtonStates(true); statusDisplay.textContent = `準(zhǔn)備開始朗讀,共${alarmList.length}條信息`; speakNextAlarm(); // 開始朗讀第一條 }); // 朗讀下一條報警信息 function speakNextAlarm() { // 如果已經(jīng)讀完所有信息或已停止,則退出 if (currentAlarmIndex >= alarmList.length || !isPlaying) { completeBatchReading(); return; } // 停止當(dāng)前正在播放的語音 if (synth.speaking) { synth.cancel(); } const currentAlarm = alarmList[currentAlarmIndex]; statusDisplay.textContent = `正在朗讀第${currentAlarmIndex + 1}/${alarmList.length}條:${currentAlarm.title}`; // 創(chuàng)建語音實(shí)例 const utterThis = new SpeechSynthesisUtterance(currentAlarm.text); // 獲取中文語音 const chineseVoices = voices.filter(v => v.lang === 'zh-CN' || v.name.includes('Chinese') ); const selectedVoice = chineseVoices.find(voice => voice.lang === 'zh-CN') || chineseVoices[0]; if (selectedVoice) { utterThis.voice = selectedVoice; } // 設(shè)置語音參數(shù) utterThis.rate = 1; // 語速 utterThis.pitch = 1; // 音調(diào) utterThis.volume = 1; // 音量 utterThis.lang = 'zh-CN'; // 語言 // 朗讀結(jié)束后處理 utterThis.onend = () => { currentAlarmIndex++; // 延遲500ms播放下一條,避免連在一起 setTimeout(speakNextAlarm, 500); }; // 朗讀出錯處理 utterThis.onerror = (event) => { statusDisplay.textContent = `第${currentAlarmIndex + 1}條朗讀出錯:${event.error}`; currentAlarmIndex++; setTimeout(speakNextAlarm, 500); }; // 開始朗讀 synth.speak(utterThis); } // 批量朗讀完成 function completeBatchReading() { isPlaying = false; statusDisplay.textContent = `所有${alarmList.length}條報警信息朗讀完成`; updateButtonStates(false); } // 暫停朗讀 pauseBtn.addEventListener('click', () => { if (synth.speaking) { synth.pause(); statusDisplay.textContent = `已暫停在第${currentAlarmIndex + 1}條`; pauseBtn.disabled = true; resumeBtn.disabled = false; } }); // 繼續(xù)朗讀 resumeBtn.addEventListener('click', () => { if (synth.paused) { synth.resume(); statusDisplay.textContent = `繼續(xù)朗讀第${currentAlarmIndex + 1}條...`; pauseBtn.disabled = false; resumeBtn.disabled = true; } }); // 停止朗讀 cancelBtn.addEventListener('click', () => { synth.cancel(); isPlaying = false; statusDisplay.textContent = '已停止朗讀'; updateButtonStates(false); }); // 更新按鈕狀態(tài) function updateButtonStates(isSpeaking) { fetchDataBtn.disabled = isSpeaking; initVoiceBtn.disabled = isSpeaking; startBtn.disabled = isSpeaking; pauseBtn.disabled = !isSpeaking || synth.paused; resumeBtn.disabled = !isSpeaking || !synth.paused; cancelBtn.disabled = !isSpeaking; } } else { // 瀏覽器不支持Web Speech API時的處理 document.getElementById('status').textContent = '抱歉,您的瀏覽器不支持語音合成功能,請使用Chrome、Edge等現(xiàn)代瀏覽器。'; // 禁用所有控制按鈕 const buttons = document.querySelectorAll('button'); buttons.forEach(button => { button.disabled = true; button.style.opacity = '0.5'; button.style.cursor = 'not-allowed'; }); } </script> </body> </html>
② 效果圖:
三、如果沒有聲音請注意:
① 是否進(jìn)行了語音包的初始化:
現(xiàn)代瀏覽器(
Chrome、Edge、Safari
等)從安全和用戶體驗(yàn)角度出發(fā),強(qiáng)制要求 “語音合成 / 音頻播放” 必須由用戶主動交互觸發(fā)(如點(diǎn)擊、觸摸、鍵盤輸入),模擬點(diǎn)擊會被認(rèn)為是腳本,沒有任何技術(shù)手段能完全 “繞過” 這一限制。
解決辦法: “弱交互觸發(fā)”
頁面加載完成后,用戶只要進(jìn)行 “極輕微的交互”(如點(diǎn)擊頁面任意位置、滾動鼠標(biāo)滾輪、按任意鍵盤鍵),就會立即觸發(fā)語音播報。
② 是否清除了其它正在播放的語音:
if (window.speechSynthesis.speaking) { window.speechSynthesis.cancel(); }
總結(jié)
到此這篇關(guān)于JS純前端實(shí)現(xiàn)瀏覽器語音播報、朗讀功能的文章就介紹到這了,更多相關(guān)JS純前端瀏覽器語音播報朗讀內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
js創(chuàng)建一個input數(shù)組并綁定click事件的方法
這篇文章主要介紹了js創(chuàng)建一個input數(shù)組并綁定click事件的方法,需要的朋友可以參考下2014-06-06JavaScript實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09JS簡單操作select和dropdownlist實(shí)例
這篇文章主要介紹了JS簡單操作select和dropdownlist的方法,以實(shí)例形式講述了js針對服務(wù)器控件select和dropdownlist的讀寫操作方法,是js與.net交互的典型應(yīng)用實(shí)例,需要的朋友可以參考下2014-11-11javascript下過濾數(shù)組重復(fù)值的代碼
javascript下過濾數(shù)組重復(fù)值的代碼...2007-09-09微信小程序?qū)崿F(xiàn)的繪制table表格功能示例
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)的繪制table表格功能,涉及微信小程序數(shù)據(jù)讀取及界面布局相關(guān)操作技巧,需要的朋友可以參考下2019-04-04