Html 代碼:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css" /> <title>文本轉語音</title> <style> body { background: #141414; } </style> </head> <body> <div class="text-white"> <div class="container text-center"> <img src="./img/speech.png" class="mb-3" /> <div class="row"> <!-- 居中對齊 --> <div class="col-md-5 mx-auto"> <form class="mb-5"> <!-- 文本輸入框 --> <div class="form-froup"> <textarea id="text-input" class="form-control mb-2" placeholder="請輸入文本內容..." ></textarea> </div> <!-- 音速 --> <div class="form-froup"> <label for="rate">音速</label> <div id="rate-value" class="badge badge-primary float-right">1</div> <input type="range" id="rate" class="custom-range" min="0.5" max="2" value="1" step="0.1" /> </div> <!-- 音調 --> <div class="form-froup"> <label for="pitch">音調</label> <div id="pitch-value" class="badge badge-primary float-right"> 1 </div> <input type="range" id="pitch" class="custom-range mb-2" min="0" max="2" value="1" step="0.1" /> </div> <!-- 選擇發音語言 --> <div class="form-froup"> <select id="voice-select" class="form-control mb-5"></select> </div> <!-- 視聽按鈕 --> <div class="form-froup"> <button class="btn btn-light btn-block mb-3">試聽</button> <p class="text-secondary"> 注意:此應用使用的是Web Speech API,爲確保正常運行,請使用最新版 Chrome 瀏覽器。 </p> </div> </form> </div> </div> </div> </div> <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script> <script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script> <script src="./main.js"></script> </body> </html>
JavaScript 代碼:
// 初始化 speechSynthesis API const synth = window.speechSynthesis // 獲取 DOM 節點 const body = document.querySelector('body') const textForm = document.querySelector('form') const textInput = document.querySelector('#text-input') const voiceSelect = document.querySelector('#voice-select') const rate = document.querySelector('#rate') const rateValue = document.querySelector('#rate-value') const pitch = document.querySelector('#pitch') const pitchValue = document.querySelector('#pitch-value') let voices = [] function getVoiceList() { voices = synth.getVoices() // 循環 voices 數組,並逐一創建一個 option voices.forEach((voice) => { const option = document.createElement('option') option.textContent = voice.name // 設置 option 屬性 option.setAttribute('data-lang', voice.lang) option.setAttribute('data-name', voice.name) voiceSelect.appendChild(option) }) } // synth.getVoices() 爲異步執行 // synth.getVoices() 方法將在 synth.onvoiceschanged 觸發時運行 // 因此須有如下語句,否則 synth.getVoices() 返回空數組 getVoiceList() if (synth.onvoiceschanged !== undefined) { synth.onvoiceschanged = getVoiceList } // 發音方法 function speakIt() { // 若正在發音則直接返回 if (synth.speaking) { return } if (textInput.value != '') { // 添加 gif 動畫 body.style.background = '#141414 url(./img/wave.gif) repeat' body.style.backgroundPositionY = '-50px' // 獲取發音文本 const speakText = new SpeechSynthesisUtterance(textInput.value) // 發音結束後觸發的方法 speakText.onend = (e) => { body.style.background = '#141414' } // 發音出錯是觸發的方法 speakText.onerror = (e) => { alert('出現錯誤,請重試。') } // 獲取 select 框當前選中的語言項並獲取其 data-name 屬性值 const selectVoice = voiceSelect.selectedOptions[0].getAttribute('data-name') // 遍歷 voices 數組,在 voice.name 中找到與上方 select 中選擇的語言一致的選項 // 並把它賦值給 speakText.voice voices.forEach((voice) => { if (voice.name === selectVoice) { speakText.voice = voice } }) // 設置發音速率 speakText.rate = rate.value // 設置發音音調 speakText.pitch = pitch.value // 開始發音 synth.speak(speakText) } } // 提交表單 textForm.addEventListener('submit', (e) => { e.preventDefault() speakIt() }) // 改變速率右側的數值 rate.addEventListener('change', (e) => { rateValue.textContent = rate.value }) // 改變音調右側的數值 pitch.addEventListener('change', (e) => { pitchValue.textContent = pitch.value })