JS30 Day 23 筆記


Posted by GL on 2023-05-23

功能

將文字轉換成語音,並透過控制條調整語音的速率以及音調。

Demo

step 1 : 建立 SpeechSynthesisUtterance 物件,并且取得 HTML 頁面中的元素


// 設置 SpeechSynthesisUtterance
const msg = new SpeechSynthesisUtterance();
let voices = [];
const voicesDropdown = document.querySelector('[name="voice"]');
// 選取器同時選取 type 為 range,以及 name 為 text 
const options = document.querySelectorAll('[type="range"],[name="text"]');
const speakButton = document.querySelector("#speak");
const stopButton = document.querySelector("#stop");

// 語音說出的内容為文字欄位中的内容
msg.text = document.querySelector('[name="text"]').value;

step 2 : 拿到當前使用裝置的語音語系,並放入下拉選單中

SpeechSynthesis.getVoices():回傳當前裝置可用的所有聲音清單(包含lang, name等屬性)

function populateVoices() {
  //  取得當前裝置的語音語系,並放入變數 voices
  voices = this.getVoices();

  // 將所有語系選項放入下拉選單中
  voicesDropdown.innerHTML = voices
    // filter 篩選出包含 en 的語系
    .filter(voice => voice.lang.includes('en'))
    // 用 map 將篩選後資料組一個個 option 選項
    .map(voice => `<option value=${voice.name}>${voice.name} (${voice.lang})</option>`)
    // 用 join 去除 map 後回傳的陣列中的逗號
    .join('');
}

// 監聽語系選單,選單改變時,觸發該事件並更新語系清單
speechSynthesis.addEventListener('voiceschanged', populateVoices);

step 3 : 設定播放相關功能

function toggle(starOver = true) {
  // 先停止播放
  speechSynthesis.cancel();
  // 如果 startOver 為 true
  if (starOver) {
     // 開始播放
    speechSynthesis.speak(msg);
  }
}

function setVoice() {
  // 設定發音的語系設爲下拉選單中切換的語系
  msg.voice = voices.find(voice => voice.name === this.value);
  // 播放
  toggle();
}


function setOption() {
// options 這個 Nodelist,
// 剛好裡面每個值的 name 取名的與 SpeechSynthesisUtterance 的物件屬性相同
// 所有可以通過下列方式,改變其 value
  msg[this.name] = this.value;
// 播放
  toggle();
}

// 監聽語系的下拉選單,可以切換發音的語系
voicesDropdown.addEventListener('change', setVoice);

// 監聽速率跟音調控制條以及文字欄位,有變動時觸發 setOption function
options.forEach(option => option.addEventListener('change', setOption));

// 監聽播放按鈕,如果被點擊,觸發 toggle function
speakButton.addEventListener('click', toggle);

// 監聽停止按鈕,如果被點擊,觸發 toggle function,並帶入參數 false
stopButton.addEventListener('click', () => toggle(false));

完整程式碼

const msg = new SpeechSynthesisUtterance();
let voices = [];
const voicesDropdown = document.querySelector('[name="voice"]');
const options = document.querySelectorAll('[type="range"],[name="text"]');
const speakButton = document.querySelector("#speak");
const stopButton = document.querySelector("#stop");
msg.text = document.querySelector('[name="text"]').value;

function populateVoices() {
  voices = this.getVoices();
  voicesDropdown.innerHTML = voices
    .filter((voice) => voice.lang.includes("en"))
    .map(
      (voice) =>
        `<option value="${voice.name}">${voice.name} (${voice.lang})</option>`
    )
    .join("");
}

function setVoice() {
  msg.voice = voices.find((voice) => voice.name === this.value);
  toggle();
}


// function play() {
//   stop();
//   speechSynthesis.speak(utterance);
// }
// function stop() {
//   speechSynthesis.cancel();
// }


function toggle(startOver = true) {
  speechSynthesis.cancel();
  if (startOver) {
    speechSynthesis.speak(msg);
  }
}

function setOption() {
  console.log(this.name, this.value);
  msg[this.name] = this.value;
  // play();
  toggle();
}

speechSynthesis.addEventListener("voiceschanged", populateVoices);
voicesDropdown.addEventListener("change", setVoice);
options.forEach((option) => option.addEventListener("change", setOption));
speakButton.addEventListener("click", toggle);
stopButton.addEventListener("click", () => toggle(false));
// speakButton.addEventListener("click", play);
// stopButton.addEventListener("click", stop);

參考資料:


#JS 30







Related Posts

物件導向基礎與 prototype

物件導向基礎與 prototype

13. Proxy

13. Proxy

[Note] React - Hooks: useMemo

[Note] React - Hooks: useMemo


Comments