Русский
Русский
English
Статистика
Реклама

Наиболее полное руководство по практическому использованию Web Speech API


В этой статье я хочу поделиться с вами результатами изучения основных возможностей Web Speech API (далее WSA).


Введение


WSA это экспериментальная технология, состоящая из двух интерфейсов: SpeechSynthesis (интерфейс для перевода текста в речь) и SpeechRecognition (интерфейс для распознавания речи).


О том, что из себя представляют названные интерфейсы и что в себя включают можно почитать на MDN или в рабочем черновике (данный черновик, в отличие от большинства спецификаций, написан более-менее человеческим языком).


Что касается поддержки, то вот что об этом говорит Can I use:




Как мы видим, SpeechSynthesis поддерживается большинством современных браузеров. К сожалению, поддержка SpeechRecognition оставляет желать лучшего. Я говорю "к сожалению", поскольку SpeechRecognition предоставляет более интересные возможности с точки зрения практического применения в веб-приложениях, в чем вы сами убедитесь после прочтения статьи. Полагаю, такая ситуация обусловлена сложностью технической реализации распознавания речи.


Мы с вами реализуем 4 простых приложения, по 2 на каждый интерфейс:


  1. Плеер для озвучивания текста со всеми возможностями, которые предоставляет SpeechSynthesis, включая выбор языка (голоса)
  2. Страницу с возможностью озвучивания ее текстового содержимого
  3. "Диктофон" для распознавания речи и ее перевода в текст с автоматическим и кастомным форматированием
  4. Одностраничное приложение с голосовым управлением, в том числе, переключение страниц, прокрутка и изменение цветовой темы (или схемы), используемой на сайте

Если вам это интересно, то прошу следовать за мной.


Плеер для озвучивания текста


Наша разметка будет выглядеть следующим образом:


<div id="wrapper">  <h1>Speech Synthesis - Player</h1>  <label    >Text:    <textarea id="textarea">Привет! Как дела?</textarea>  </label>  <label    >Voice:    <select id="select"></select>  </label>  <label    >Volume:    <input id="volume" type="range" min="0" max="1" step="0.1" value="1" />    <span>1</span>  </label>  <label    >Rate:    <input id="rate" type="range" min="0" max="3" step="0.5" value="1" />    <span>1</span>  </label>  <label    >Pitch:    <input id="pitch" type="range" min="0" max="2" step="0.5" value="1" />    <span>1</span>  </label>  <div id="buttons">    <button class="speak">Speak</button>    <button class="cancel">Cancel</button>    <button class="pause">Pause</button>    <button class="resume">Resume</button>  </div></div>

У нас имеется поле для ввода текста (textarea), который мы будем озвучивать; выпадающий список (select) для выбора голоса, которым будет озвучиваться текст; инпуты-диапазоны для установки громкости (volume), скорости (rate) воспроизведения и высоты голоса (pitch), а также кнопки для запуска (speak), отмены (cancel), приостановления (pause) и продолжения (resume) воспроизведения. В общем, ничего особенного.


Обратите внимание на атрибуты инпутов min, max, step и value. Значения данных атрибутов взяты из черновика спецификации, но некоторые из них отданы на откуп производителям браузеров, т.е. зависят от конкретной реализации. Также обратите внимание на наличие почти у всех элементов идентификаторов. Мы будем использовать эти id для прямого доступа к элементам в скрипте в целях сокращения кода, однако в реальных приложениях так лучше не делать во избежание загрязнения глобального пространства имен.


Переходим к JavaScript. Создаем экземпляр SpeechSynthesisUtterance ("utterance" можно перевести как "выражение текста словами"):


const U = new SpeechSynthesisUtterance()

Пытаемся получить доступные голоса (именно "пытаемся", поскольку в первый раз, по крайней мере, в Chrome возвращается пустой массив):


let voices = speechSynthesis.getVoices()

При вызове метода getVoices() возникает событие voiceschanged. Обрабатываем это событие для "настоящего" получения голосов и формирования выпадающего списка:


speechSynthesis.onvoiceschanged = () => {  voices = speechSynthesis.getVoices()  populateVoices(voices)}

Объект голоса (назовем его так) выглядит следующим образом:


0: SpeechSynthesisVoice  default: true  lang: "de-DE"  localService: false  name: "Google Deutsch"  voiceURI: "Google Deutsch"

Реализуем функцию формирования выпадающего списка:


function populateVoices(voices) {  // Перебираем голоса и создаем элемент `option` для каждого  // Текстовым содержимым `option` является название голоса, а значением - индекс голоса в массиве голосов  voices.forEach((voice, index) => {    select.options[index] = new Option(voice.name, index)  })  // Делаем голосом по умолчанию `Google русский`  // Он нравится мне больше, чем голос от `Microsoft`  const defaultVoiceIndex = voices.findIndex(    (voice) => voice.name === 'Google русский'  )  select.selectedIndex = defaultVoiceIndex  // После этого инициализируем обработчики событий  initializeHandlers()}

Функция инициализации обработчиков событий выглядит так:


function initializeHandlers() {  // Ниже перечислены почти все события, которые возникают при работе с рассматриваемым интерфейсом  U.onstart = () => console.log('Started')  U.onend = () => console.log('Finished')  U.onerror = (err) => console.error(err)  // Мне не удалось добиться возникновения этих событий  U.onpause = () => console.log('Paused')  U.onresume = () => console.log('Resumed')  // Обработка изменения настроек  wrapper.onchange = ({ target }) => {    if (target.type !== 'range') return    handleChange(target)  }  // Обработка нажатия кнопок  buttons.addEventListener('click', ({ target: { className } }) => {    // SpeechSynthesis предоставляет таким методы как `speak()`, `cancel()`, `pause()` и `resume()`    // Вызов метода `speak()` требует предварительной подготовки    // Кроме того, мы проверяем наличие текста в очереди на озвучивание с помощью атрибута `speaking`    // Есть еще два атрибута: `pending` и `paused`, но они не всегда возвращают корректные значения    switch (className) {      case 'speak':        if (!speechSynthesis.speaking) {          convertTextToSpeech()        }        break      case 'cancel':        return speechSynthesis.cancel()      case 'pause':        return speechSynthesis.pause()      case 'resume':        return speechSynthesis.resume()      default:        return    }  })}

Обработка изменения настроек:


function handleChange(el) {  el.nextElementSibling.textContent = el.value}

Функция преобразования текста в речь:


function convertTextToSpeech() {  // Получаем текст  const trimmed = textarea.value.trim()  if (!trimmed) return  // Передаем его экземпляру `SpeechSynthesisUtterance`  U.text = trimmed  // Получаем выбранный голос  const voice = voices[select.value]  // Передаем голос и другие настройки экземпляру  U.voice = voice  // язык  U.lang = voice.lang  // громкость  U.volume = volume.value  // скорость  U.rate = rate.value  // высота голоса  U.pitch = pitch.value  // Запускаем озвучивание!  speechSynthesis.speak(U)}

После установки всех настроек наш экземпляр SpeechSynthesisUtterance выглядит следующим образом:


SpeechSynthesisUtterance  lang: "ru-RU"  onboundary: null  onend: () => console.log('Finished')  onerror: (err) => console.error(err)  onmark: null  onpause: () => console.log('Paused')  onresume: () => console.log('Resumed')  onstart: () => console.log('Started')  pitch: 1  rate: 1  text: "Привет! Как дела?"  voice: SpeechSynthesisVoice { voiceURI: "Google русский", name: "Google русский", lang: "ru-RU", localService: false, default: false }  volume: 1

Уже в процессе написания статьи я решил добавить управление с помощью клавиатуры:


window.onkeydown = ({ key }) => {  switch (key.toLowerCase()) {    case 's':      if (!speechSynthesis.speaking) {        convertTextToSpeech()      }      break    case 'c':      return speechSynthesis.cancel()    case 'p':      return speechSynthesis.pause()    case 'r':      return speechSynthesis.resume()    default:      return  }}

Поиграть с кодом можно здесь:



Страница с возможность озвучивания текстового содержимого


Что касается SpeechSynthesis, то с точки зрения практического использования данного интерфейса, сложно что-то придумать, кроме создания страниц с возможностью озвучивания размещенного на них текста. Этим мы и займемся.


Преимущество использования SpeechSynthesis заключается в реальном улучшении доступности контента приложения для людей с ограниченными возможностями здоровью. Основная проблема, на мой взгляд, состоит в том, что пользователи не привыкли к подобным приложениям, следовательно, им надо каким-то образом намекнуть о существовании такой возможности. Причем, сделать это нужно в очень ясной, но при этом ненавязчивой форме.


Наша разметка будет выглядеть так:


<div id="wrapper">  <h1>Speech Synthesis - Page Reader</h1>  <div>    <button class="play" tabindex="1"></button>    <p>      JavaScript  мультипарадигменный язык программирования. Поддерживает      объектно-ориентированный, императивный и функциональный стили.      Является реализацией спецификации ECMAScript (стандарт ECMA-262).    </p>  </div>  <div>    <button class="play" tabindex="2"></button>    <p>      JavaScript обычно используется как встраиваемый язык для программного      доступа к объектам приложений. Наиболее широкое применение находит в      браузерах как язык сценариев для придания интерактивности      веб-страницам.    </p>  </div>  <div>    <button class="play" tabindex="3"></button>    <p>      Основные архитектурные черты: динамическая типизация, слабая      типизация, автоматическое управление памятью, прототипное      программирование, функции как объекты первого класса.    </p>  </div></div>

У нас имеется три блока (div) с кнопками для озвучивания текста (play) и, собственно, текстом, который будет озвучиваться (первые три абзаца статьи по JavaScript из Википедии). Обратите внимание, что я добавил кнопкам атрибут tabindex, чтобы переключаться между ними с помощью tab и нажимать с помощью space. Однако, имейте ввиду, что использовать атрибут tabindex не рекомендуется по причине того, что браузер использует переключение фокуса с помощью tab для повышения доступности.


С вашего позволения, я приведу код скрипта целиком:


// Это часть должна быть вам знакома по предыдущему примеруlet voices = speechSynthesis.getVoices()let defaultVoicespeechSynthesis.onvoiceschanged = () => {  voices = speechSynthesis.getVoices()  defaultVoice = voices.find((voice) => voice.name === 'Google русский')  wrapper.addEventListener('click', handleClick)  window.addEventListener('keydown', handleKeydown)}const PLAY = 'play'const PAUSE = 'pause'const RESUME = 'resume'function handleClick({ target }) {  switch (target.className) {    case PLAY:      // При нажатии кнопки `play` в момент озвучивания другого текста,      // нам нужно прекратить текущее озвучивание перед началом нового      speechSynthesis.cancel()      const { textContent } = target.nextElementSibling      // Об этой части см. ниже      textContent.split('.').forEach((text) => {        const trimmed = text.trim()        if (trimmed) {          const U = getUtterance(target, text)          speechSynthesis.speak(U)        }      })      break    case PAUSE:      // CSS-класс кнопки отвечает за отображаемую рядом с ней иконку      // ``- ожидание/стоп, `` - озвучивание/воспроизведение, `` - пауза      // иконка `` используется в качестве индикатора кнопки, находящейся в фокусе      target.className = RESUME      speechSynthesis.pause()      break    case RESUME:      target.className = PAUSE      speechSynthesis.resume()      break    default:      break  }}// При нажатии `escape` прекращаем озвучивание текста// Можете добавить свои контролыfunction handleKeydown({ code }) {  switch (code) {    case 'Escape':      return speechSynthesis.cancel()    default:      break  }}function getUtterance(target, text) {  const U = new SpeechSynthesisUtterance(text)  U.voice = defaultVoice  U.lang = defaultVoice.lang  U.volume = 1  U.rate = 1  U.pitch = 1  // Я специально не стал скрывать смену иконок при начале/окончании воспроизведения  U.onstart = () => {    console.log('Started')    target.className = PAUSE  }  U.onend = () => {    console.log('Finished')    target.className = PLAY  }  U.onerror = (err) => console.error(err)  return U}

Тонкий момент в приведенном коде это преобразование озвучиваемого текста в массив предложений (разделителем является точка (.)), перебор массива, и воспроизведение каждого предложения по отдельности (точнее, помещение всех предложений одного за другим в очередь на озвучивание) textContent.split('.').forEach(...). Причина такого решения заключается в том, что озвучивание длинного текста тихо обрывается примерно на 220 символе (в Chrome). Черновик спецификации для такого случае предусматривает специальную ошибку text-to-long (текст слишком длинный), но данной ошибки не возникает, озвучивание просто резко прекращается, причем, для восстановления работы SpeechSynthesis зачастую приходится перезагружать вкладку (при запущенном сервере для разработки даже это не всегда срабатывает). Возможно, вам удастся найти другое решение.


Поиграть с кодом можно здесь:



"Диктофон" для распознавания речи


После того, как мы обстоятельно рассмотрели SpeechSynthesis, можно переходить ко второму, немного более сложному, но, вместе с тем, и более интересному интерфейсу, входящему в состав WSA SpeechRecoginition.


Вот наша начальная разметка:


<div id="wrapper">  <h1>Speech Recognition - Dictaphone</h1>  <textarea id="final_text" cols="30" rows="10"></textarea>  <input type="text" id="interim_text" />  <div id="buttons">    <button class="start">Старт</button>    <button class="stop">Стоп</button>    <button class="abort">Сброс</button>    <button class="copy">Копия</button>    <button class="clear">Очистить</button>  </div></div>

У нас имеется поле для вставки финального (распознанного) текста (final_text) и поле для вставки промежуточного (находящегося в процессе распознавания) текста (interim_text), а также панель управления (buttons). Выбор элементов textarea и input для хранения текста обусловлен как вариативностью промежуточных результатов, которые меняются на лету, так и необходимостью внесения небольших правок в распознанный текст, что связано с естественным несовершенством перевода устной речи в текст. Стоит отметить, что, в целом, Chrome очень неплохо справляется с задачей распознавания речи и автоматическим форматированием распознанного текста.


Кроме кнопок для управления распознаванием речи (start, stop и abort), мы реализуем возможность копирования распознанного текста в буфер обмена с помощью Clipboard API и очистки соответствующего поля.


Начнем с определения переменных для финального текста и индикатора распознавания:


let final_transcript = ''let recognizing = false

Далее, создаем и настраиваем экземпляр SpeechRecognition:


// Напоминаю, что `WSA`, в целом, и, особенно, `SpeechRecognition` являются экпериментальнымиconst speechRecognition =  window.SpeechRecognition || window.webkitSpeechRecognition// Создаем экземпляр `SpeechRecognition`const recognition = new speechRecognition()// Свойство `continuous` определяет, продолжается ли распознавание речи после получения первого финального результатаrecognition.continuous = true// обработка промежуточных результатовrecognition.interimResults = true// максимальное количество альтернатив распознанного словаrecognition.maxAlternatives = 3// языкrecognition.lang = 'ru-RU'

Добавляем обработчики событий запуска, ошибки и окончания распознавания речи:


recognition.onstart = () => {  console.log('Распознавание голоса запущено')}recognition.onerror = ({ error }) => {  console.error(error)}recognition.onend = () => {  console.log('Распознавание голоса закончено')  // Снова запускаем распознавание, если индикатор имеет значение `true`  if (!recognizing) return  recognition.start()}

SpeechRecognition хорошо справляется с распознаванием слов, фраз и целых предложений, он даже выполняет вполне уместную "капитализацию" строки. Но он не "понимает" знаков препинания. Для того, чтобы помочь ему в этом определим словарь в виде объекта:


const DICTIONARY = {  точка: '.',  запятая: ',',  вопрос: '?',  восклицание: '!',  двоеточие: ':',  тире: '-',  абзац: '\n',  отступ: '\t'}

Предполагается, что для решение задач, связанных с форматированием, будут использоваться специальные объекты SpeechGrammar и SpeechGrammarList, а также специальный синтаксис JSpeech Grammar Format, однако, в данной статье мы ограничимся обычным объектом.


Вы можете добавить в словарь любые пары ключ/значение, которые посчитаете нужными. Обратите внимание на то, что все ключи в нашем словаре представлены одним словом. Дело в том, что рассматриваемый интерфейс плохо справляется с ключами, которые состоят более чем из одного слова, например, "вопросительный знак", "восклицательный знак" и т.п. Я понимаю, что пара вопрос: '?' не лучшее решение, но для примера сойдет.


Также нам потребуются две функции для редактирования промежуточного и финального результатов:


function editInterim(s) {  return s    .split(' ')    .map((word) => {      word = word.trim()      return DICTIONARY[word.toLowerCase()] ? DICTIONARY[word.toLowerCase()] : word    })    .join(' ')}function editFinal(s) {  return s.replace(/\s{1,}([\.+,?!:-])/g, '$1')}

Функция editInterim() разбивает фразу на массив слов, перебирает слова, удаляет пробелы в начале и конце слова и заменяет символом из словаря при совпадении. Обратите внимание, что мы приводим слово в нижний регистр только при поиске совпадения. Если мы сделаем это в строке word = word.trim(), то нивелируем автоматическую "капитализацию" строки, выполняемую браузером.


Функция editFinal() удаляет пробел перед каждым из указанных символов мы разделяем слова пробелами при объединении в функции editInterim(). Следует отметить, что по умолчанию каждое распознанное слово с двух сторон обрамляется пробелами.


Итак, событие, которое интересует нас больше всего это result. Реализуем его обработку:


recognition.onresult = (e) => {  // Промежуточные результаты обновляются на каждом цикле распознавания  let interim_transcript = ''  // Перебираем результаты с того места, на котором остановились в прошлый раз  for (let i = e.resultIndex; i < e.results.length; i++) {    // Атрибут `isFinal` является индикатором того, что речь закончилась    if (e.results[i].isFinal) {      // Редактируем промежуточные результаты      const result = editInterim(e.results[i][0].transcript)      // и добавляем их к финальному результату      final_transcript += result    } else {      // В противном случае, записываем распознанные слова в промежуточный результат      interim_transcript += e.results[i][0].transcript    }  }   // Записываем промежуточные результаты в `input`  interim_text.value = interim_transcript  // Редактируем финальный результат  final_transcript = editFinal(final_transcript)  // и записываем его в `textarea`  final_text.value = final_transcript}

Событие распознавания выглядит следующим образом:


SpeechRecognitionEvent  bubbles: false  cancelBubble: false  cancelable: false  composed: false  currentTarget: SpeechRecognition {grammars: SpeechGrammarList, lang: "ru-RU", continuous: true, interimResults: true, maxAlternatives: 3, }  defaultPrevented: false  emma: null  eventPhase: 0  interpretation: null  isTrusted: true  path: []  resultIndex: 1  // здесь нас интересуют только результаты  results: SpeechRecognitionResultList {0: SpeechRecognitionResult, 1: SpeechRecognitionResult, length: 2}  returnValue: true  srcElement: SpeechRecognition {grammars: SpeechGrammarList, lang: "ru-RU", continuous: true, interimResults: true, maxAlternatives: 3, }  target: SpeechRecognition {grammars: SpeechGrammarList, lang: "ru-RU", continuous: true, interimResults: true, maxAlternatives: 3, }  timeStamp: 59862.61999979615  type: "result"

Результаты (SpeechRecognitionResultList) выглядят так:


results: SpeechRecognitionResultList  0: SpeechRecognitionResult    0: SpeechRecognitionAlternative      confidence: 0.7990190982818604      transcript: "привет"    isFinal: true    length: 1  length: 1

Вот почему для получения результата мы обращаемся к e.results[i][0].transcript. На самом деле, поскольку мы указали maxAlternatives = 3, во всех результатах будет представлено по три SpeechRecognitionAlternative. Первым (с индексом 0) всегда будет наиболее подходящий результат с точки зрения браузера.


Все, что нам осталось сделать, это реализовать обработку нажатия кнопок:


buttons.onclick = ({ target }) => {  switch (target.className) {    case 'start':      // Обнуляем переменную для финального результата      final_transcript = ''      // Запускаем распознавание      recognition.start()      // Устанавливаем индикатор распознавания в значение `true`      recognizing = true      // Очищаем `textarea`      final_text.value = ''      // Очищаем `input`      interim_text.value = ''      break    case 'stop':      // Останавливаем распознавание      recognition.stop()      // Устанавливаем значение индикатора распознавания в значение `false`      recognizing = false      break    case 'abort':      // Прекращаем распознавание      recognition.abort()      recognizing = false      break    case 'copy':      // Копируем текст из `textarea` в буфер обмена      navigator.clipboard.writeText(final_text.value)      // Сообщаем об этом пользователю      target.textContent = 'Готово'      const timerId = setTimeout(() => {        target.textContent = 'Копия'        clearTimeout(timerId)      }, 3000)      break    case 'clear':      // Обнуляем переменную для финального результата      final_transcript = ''      // Очищаем `textarea`      final_text.value = ''      break    default:      break  }}

Тестируем. Нажимаем на кнопку "Старт", дожидаемся, когда красная точка рядом с фавиконкой перестанет мигать (о готовности к распознаванию можно сообщать пользователю через обработку события speechstart), произносим фразу (произношение должно быть максимально четким и ясным), например, "привет точка как дела вопрос". В input на какое-то время появляется "Привет точка Как дела вопрос", затем этот промежуточный результат редактируется и переносится в textarea в виде "Привет. Как дела?". Отлично, наша машина нас понимает.


Поиграть с кодом можно здесь:



Подумал о том, что было бы неплохо реализовать функцию для удаления последнего распознанного слова на тот случай, если результат распознавания получился некорректным. Для этого потребуется пара удалить: () => removeLastWord() (если добавить эту пару в словарь, то потребуется дополнительная проверка typeof DICTIONARY[word] === 'function') и примерно такая операция:


function removeLastWord() {  const oldStr = final_text.value  const newStr = oldStr.substring(0, oldStr.lastIndexOf(' '))  final_text.value = newStr}

Однако, добавлять операцию в словарь плохая идея, для этого лучше использовать отдельный объект, чем мы и займемся в следующем разделе.


Одностраничное приложение с голосовым управлением


Такой вариант использования SpeechRecognition, на мой взгляд, представляет наибольший интерес с точки зрения возможности применения данной технологии в реальных приложениях.


Мы будем генерировать страницы программным способом, поэтому разметка нам не потребуется.


Создаем директорию pages с тремя файлами:


// home.jsexport default /*html*/ `<div id="wrapper">  <div>Section 1</div>  <div>Section 2</div>  <div>Section 3</div>  <div>Section 4</div>  <div>Section 5</div>  <div>Section 6</div>  <div>Section 7</div>  <div>Section 8</div>  <div>Section 9</div></div>`// product.jsexport default /*html*/ `<h1>This is the Product Page</h1>`// about.jsexport default /*html*/ `<h1>This is the About Page</h1>`

Основной скрипт начинается с импорта страниц и генерации домашней страницы:


import HomePage from './pages/home.js'import ProductPage from './pages/product.js'import AboutPage from './pages/about.js'const { body } = documentbody.innerHTML = HomePage

Нам потребуются константы для прокрутки страницы и объект с операциями:


// Константы для прокруткиconst DOWN = 'down'const UP = 'up'const RIGHT = 'right'const LEFT = 'left'// Операцииconst ACTIONS = {  // операции переключения страниц  home: () => (body.innerHTML = HomePage),  product: () => (body.innerHTML = ProductPage),  about: () => (body.innerHTML = AboutPage),  // операции прокрутки  down: () => scroll(DOWN),  up: () => scroll(UP),  left: () => scroll(LEFT),  right: () => scroll(RIGHT),  // операции переключения цветовой темы  light: () => body.removeAttribute('class'),  dark: () => (body.className = 'dark')}

Далее следуют настройки SpeechRecognition и обработчики событий начала, ошибки и окончания распознавания, аналогичные рассмотренным в предыдущем разделе (кроме настройки языка для управления страницей мы будем использовать американский английский: recognition.lang = 'en-US').


Обработка события result:


recognition.onresult = (e) => {  // Перебираем результаты  for (let i = e.resultIndex; i < e.results.length; i++) {    if (e.results[i].isFinal) {      const result = e.results[i][0].transcript.toLowerCase()      // Выводим слова в консоль, чтобы убедиться в корректности распознавания (как выяснилось, слово `product` очень плохо распознается, возможно, у меня проблемы с его правильным произношением)      console.log(result)      // Преобразуем фразу в массив, перебираем слова и выполняем соответствующие операции      result        .split(' ')        .forEach((word) => {          word = word.trim().toLowerCase()          // ACTION[word] - это функция          return ACTIONS[word] ? ACTIONS[word]() : ''        })    }  }}

Функция для выполнения прокрутки выглядит следующим образом:


function scroll(direction) {  let newPosition  switch (direction) {    case DOWN:      newPosition = scrollY + innerHeight      break    case UP:      newPosition = scrollY - innerHeight      break    case RIGHT:      newPosition = scrollX + innerWidth      break    case LEFT:      newPosition = scrollX - innerWidth      break    default:      break  }  if (direction === DOWN || direction === UP) {    scrollTo({      top: newPosition,      behavior: 'smooth'    })  } else {    scrollTo({      left: newPosition,      behavior: 'smooth'    })  }}

Наконец, добавляем обработку нажатия клавиш клавиатуры:


window.addEventListener('keydown', (e) => {  e.preventDefault()  switch (e.code) {    // Нажатие пробела запускает распознавание    case 'Space':      recognition.start()      recognizing = true      break    // Нажатие `escape` останавливает распознавание    case 'Escape':      recognition.stop()      recognizing = false    default:      break  }})

Проверяем работоспособность приложения. Нажимаем пробел, дожидаемся готовности браузера к распознаванию речи, произносим следующие фразы:


  • home, product, about переключение страниц
  • dark, light переключение цветовой темы
  • down, up, left, right выполнение прокрутки к соответствующему разделу страницы (имеет видимый эффект только на домашней странице, иногда приходится добавлять слово scroll, например, scroll down)

Поиграть с кодом можно здесь:



Заключение


Как мы видим, WSA существенно расширяет арсенал JavaScript, связанный с добавлением динамики, "оживлением" веб-страниц. Сложно сказать, насколько широкое распространение получит данная технология и насколько активно она будет использоваться. Несмотря на то, что WSA это технология общего назначения, очевидно, что она внесет серьезный вклад, в первую очередь, в развитие так называемого доступного Интернета.


Мы с вами рассмотрели все основные возможности, предоставляемые WSA. Единственное, что мы не сделали, это не реализовали совместное использование SpeechSynthesis и SpeechRecognition. Первое, что приходит на ум это голосовой помощник, который реагирует на ключевые слова, встречающиеся во фразе, произнесенной пользователем, отвечая определенными шаблонами и задавая уточняющие вопросы, при необходимости. Более простой вариант: волшебный шар задаешь любой вопрос, предполагающий однозначный ответ, программа какое-то время "думает" и отвечает да, нет или, например, не знаю, спросите позже. Пусть это будет вашим домашним заданием, все необходимые знания для этого у вас имеются. Обязательно поделитесь результатом в комментариях.




Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Источник: habr.com
К списку статей
Опубликовано: 27.05.2021 10:19:54
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании маклауд

Разработка веб-сайтов

Javascript

Api

Webspeechapi

Сайты

Vds

Vps

Быстрые серверы

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru