JS純前端實現(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);
});
// 默認選擇第一個中文語音
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;
}
// 顯示當前速率、音調(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);
// 設置語音
const selectedVoice = voices.find(voice => voice.name === voiceSelect.value);
if (selectedVoice) {
utterThis.voice = selectedVoice;
}
// 設置語音參數(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>
② 效果圖:

二、朗讀多條文本:
① 語音有默認值:
<!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>
<!-- 報警信息列表預覽 -->
<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">請點擊"獲取后端數(shù)據(jù)"按鈕開始</div>
</body>
<script>
// 檢查瀏覽器是否支持Web Speech API
if ('speechSynthesis' in window) {
const synth = window.speechSynthesis;
let voices = [];
let isVoiceReady = false; // 語音是否準備就緒
let currentAlarmIndex = 0; // 當前播放的報警索引
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)絡延遲
setTimeout(() => {
// 模擬后端返回的數(shù)據(jù)
const mockData = {
success: true,
data: [
{
"id": 1,
"title": "空調(diào)異常警報",
"text": "25樓北區(qū)空調(diào)參數(shù)異常:總有功功率實際值5.79kW,異常狀態(tài)累計15分0秒",
"type": 1,
"typeText": "輕微",
"timestamp": "2023-10-15 09:23:45"
},
{
"id": 2,
"title": "配電柜溫度過高",
"text": "B棟配電室3號柜溫度超標:當前溫度62°C(閾值55°C),持續(xù)8分30秒",
"type": 2,
"typeText": "嚴重",
"timestamp": "2023-10-15 10:15:22"
},
{
"id": 3,
"title": "水泵壓力異常",
"text": "地下二層供水系統(tǒng)壓力異常:當前壓力0.25MPa(正常范圍0.3-0.5MPa),持續(xù)12分鐘",
"type": 1,
"typeText": "輕微",
"timestamp": "2023-10-15 11:07:18"
},
{
"id": 4,
"title": "消防水箱水位不足",
"text": "1號樓消防水箱水位過低:當前水位1.2m(最低警戒線1.5m),請立即處理",
"type": 3,
"typeText": "緊急",
"timestamp": "2023-10-15 13:45:03"
}
]
};
resolve(mockData);
}, 1500); // 1.5秒延遲,模擬真實網(wǎng)絡請求
});
}
// 在頁面上顯示報警信息列表
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}個中文語音包,點擊"開始批量朗讀"按鈕`;
console.log('可用中文語音:', chineseVoices);
} catch (err) {
statusDisplay.textContent = `語音初始化失?。?{err.message}`;
initVoiceBtn.disabled = false;
}
});
// 獲取語音列表(帶重試機制)
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 = `準備開始朗讀,共${alarmList.length}條信息`;
speakNextAlarm(); // 開始朗讀第一條
});
// 朗讀下一條報警信息
function speakNextAlarm() {
// 如果已經(jīng)讀完所有信息或已停止,則退出
if (currentAlarmIndex >= alarmList.length || !isPlaying) {
completeBatchReading();
return;
}
// 停止當前正在播放的語音
if (synth.speaking) {
synth.cancel();
}
const currentAlarm = alarmList[currentAlarmIndex];
statusDisplay.textContent = `正在朗讀第${currentAlarmIndex + 1}/${alarmList.length}條:${currentAlarm.title}`;
// 創(chuàng)建語音實例
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ù)
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>
② 效果圖:

三、如果沒有聲音請注意:
① 是否進行了語音包的初始化:
現(xiàn)代瀏覽器(
Chrome、Edge、Safari等)從安全和用戶體驗角度出發(fā),強制要求 “語音合成 / 音頻播放” 必須由用戶主動交互觸發(fā)(如點擊、觸摸、鍵盤輸入),模擬點擊會被認為是腳本,沒有任何技術手段能完全 “繞過” 這一限制。
解決辦法: “弱交互觸發(fā)”
頁面加載完成后,用戶只要進行 “極輕微的交互”(如點擊頁面任意位置、滾動鼠標滾輪、按任意鍵盤鍵),就會立即觸發(fā)語音播報。
② 是否清除了其它正在播放的語音:
if (window.speechSynthesis.speaking) {
window.speechSynthesis.cancel();
}
總結(jié)
到此這篇關于JS純前端實現(xiàn)瀏覽器語音播報、朗讀功能的文章就介紹到這了,更多相關JS純前端瀏覽器語音播報朗讀內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
js創(chuàng)建一個input數(shù)組并綁定click事件的方法
這篇文章主要介紹了js創(chuàng)建一個input數(shù)組并綁定click事件的方法,需要的朋友可以參考下2014-06-06
JavaScript實現(xiàn)飛機大戰(zhàn)游戲
這篇文章主要為大家詳細介紹了JavaScript實現(xiàn)飛機大戰(zhàn)游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
微信小程序?qū)崿F(xiàn)的繪制table表格功能示例
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)的繪制table表格功能,涉及微信小程序數(shù)據(jù)讀取及界面布局相關操作技巧,需要的朋友可以參考下2019-04-04

