<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="style.css" />
    <title>Project #20 - The Text Reader App</title>
  </head>
  <body>
    <div class="container">
      <textarea name="text" id="text"></textarea>
      <!-- controls wrapper -->
      <div class="controls-wrapper">
        <div>
          <label for="speed">Speed</label>
          <input
            type="number"
            name="speed"
            id="speed"
            min="0.1"
            max="10"
            step="0.1"
            value="1"
          />
        </div>
        <button class="read">Read</button>
        <button class="pause">Pause</button>
        <button class="stop">Stop</button>
      </div>
    </div>
    <!-- -------------------------- -->
    <!-- JS File -->
    <script src="app.js"></script>
  </body>
</html>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: Arial, Helvetica, sans-serif;
  background-color: #221f3b;
  color: white;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

textarea {
  height: 500px;
  width: 1000px;
  color: black;
  border: none;
  outline: none;
  padding: 15px;
  font-size: 20px;
  margin-bottom: 25px;
}

textarea:focus {
  background-color: #5d54a4;
  color: white;
}

.controls-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
}

.controls-wrapper > * {
  margin-right: 25px;
}

.controls-wrapper label {
  font-size: 25px;
  margin-right: 8px;
}

.controls-wrapper input {
  padding: 7px 15px;
  outline: 0;
  border: 0;
  color: white;
  background-color: #5d54a4;
  font-size: 20px;
}

.controls-wrapper button {
  background: none;
  border: none;
  outline: none;
  color: white;
  background-color: #5d54a4;
  padding: 10px 20px;
  cursor: pointer;
  font-size: 20px;
}
const textDisplay = document.querySelector("#text");
const speedBtn = document.querySelector("#speed");
const readBtn = document.querySelector(".read");
const pauseBtn = document.querySelector(".pause");
const stopBtn = document.querySelector(".stop");
let currentChar;

// Rreading Functionality
readBtn.addEventListener("click", function () {
  readText(textDisplay.value);
});

// Pausing Functionality
pauseBtn.addEventListener("click", pauseText);

// Stopping Functionality
stopBtn.addEventListener("click", stopText);

// Speed Input Functionality
speedBtn.addEventListener("input", function () {
  stopText();//현재 속도로 읽는 건 멈추고 
  readText(utterance.text.substring(currentChar));
});

/*
https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance
*/
const utterance = new SpeechSynthesisUtterance();
//speech Api 생성
utterance.addEventListener("end", function () {
  //발언이 완료. 글자 입력 허용
  textDisplay.disabled = false;
});

utterance.addEventListener("boundary", function (e) {
  //발언이 문장 경계에 도달하면 시행
  currentChar = e.charIndex;
});

// readText Function
function readText(testText) {
  //멈춤상태에서도 speaking은 값이 참이면 실행, 그러면 다시 음성 재개.
  if (speechSynthesis.paused && speechSynthesis.speaking) {
    return speechSynthesis.resume();
  }
  //멈추지 않은 상태에서 말한다 --- 음성이 진행중.
  if (speechSynthesis.speaking) return;

  utterance.text = testText;
  utterance.rate = speedBtn.value || 1;
  //입력 못하게 막기.
  textDisplay.disabled = true;
  speechSynthesis.speak(utterance);
}

// pauseText Function
function pauseText() {
  if (speechSynthesis.speaking)
  //말하는 중에 멈춤 누르면 멈추기.
  speechSynthesis.pause();
}

// stopText Function
function stopText() {
  speechSynthesis.resume();
  speechSynthesis.cancel();
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.