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 })