Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions 23 - Speech Synthesis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# 23 Speech Synthesis 中文指南

> 本篇作者:©[大史快跑Dashrun](https://github.com/dashrun)——Chinasoft Frontend Developer

> 简介:[JavaScript30](https://javascript30.com) 是 [Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 23 篇。完整指南在 [GitHub](https://github.com/soyaine/JavaScript30),喜欢请 Star 哦♪(^∇^*)

> 创建时间:2017-09-20
最后更新:2017-09-22

## 挑战任务
初始文档`index-start.html`提供了一个阅读器,你需要完成如下编程任务:
1.使用相应的WebAPI接口获得浏览器支持的语言种类列表,并填充至页面的下拉菜单中,选择中文;
2.在文本域中输入对应语言的文字,点击`speak`按钮后浏览器会阅读输入的文字;
3.在浏览器阅读时,点击`stop`按钮,浏览器会停止阅读;
4.拖动`rate`和`pitch`滑块可改变阅读速度和音高。

## 实现效果
![结果展示](https://github.com/dashrun/vanilla-javascript-30/blob/master/23%20-%20Speech%20Synthesis/effects.png)

## 相关知识
1.`SpeechSynthesisUtterance`接口
本接口用于设置阅读器阅读的配置参数,包括语言,阅读速度,语调等,实例化`SpeechSynthesisUtterance`后,可以通过为其属性赋值来完成参数配置,详细信息请直接参考MDN中的[SpeechSynthesisUtterance接口说明](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance)。
2.`SpeechSynthesis`接口
本接口用于控制阅读器行为,包括获取浏览器支持的朗读语言,文本朗读,暂停,停止等,接口属性中定义有paused,speaking等只读属性来表明当前的状态,详细使用方式请参考MDN中的[SpeechSynthesis接口说明](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis)。

## 编程思路
本次编程任务使用相应接口的最基本功能即可实现,编程中根据挑战任务中的说明逐步实现即可。

## 过程指南
1.取得`speechSynthesis`对象,并取得浏览器支持的朗读语言,将所有支持的选项动态添加至下拉列表
```js
const synth = window.speechSynthesis;

//将获取支持语言并添加至下拉列表的代码段封装在一个函数中
function getSupportVoice() {
voices = synth.getVoices();//获取支持的语言
for(i = 0; i < voices.length ; i++) {
var option = document.createElement('option');
option.textContent = voices[i].name + ' (' + voices[i].lang + ')';

if(voices[i].default) {
option.textContent += ' -- DEFAULT';
}

option.setAttribute('data-lang', voices[i].lang);
option.setAttribute('data-name', voices[i].name);
voicesDropdown.appendChild(option);
}
}

//经测试直接执行getSupportVoice()时无法获得预期效果,须由事件触发该函数。
synth.addEventListener('voiceschanged', getSupportVoice);
```
2.点击`speak`按钮后朗读(为方便说明,以下代码段与所提供的完成代码顺序不完全一致)
```js
//实例化配置对象
const msg = new SpeechSynthesisUtterance();

//定义一段默认朗读内容
msg.text = '你能说中文吗';

//点击speak按钮时阅读文字
function speak() {
console.log(voicesDropdown.value);
synth.speak(msg);
}

//将阅读函数绑定至`speak`按钮的点击事件上
speakButton.addEventListener('click', speak);
```
3.点击`stop`按钮停止朗读
```js
//停止朗读
function stopSpeak(){
synth.cancel();
}
//将停止朗读函数绑定至`stop`按钮的点击事件上
stopButton.addEventListener('click', stopSpeak);
```
4.参数配置可更改
```js
//index-start.html中提供的选择器将返回rate值,pitch值以及阅读内容对应的DOM元素
const options = document.querySelectorAll('[type="range"], [name="text"]');
//将阅读参数赋值至msg的同名实例属性
function paramChange(){
msg[this.name] = this.value;
}
options.forEach(opt => opt.addEventListener('change', paramChange));
```




Binary file added 23 - Speech Synthesis/effects.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions 23 - Speech Synthesis/index-finished-Dashrun.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Speech Synthesis</title>
<link rel="stylesheet" href="style.css">
</head>
<body>

<div class="voiceinator">

<h1>听说 5000</h1>

<select name="voice" id="voices">
<option value="">Select A Voice</option>
</select>

<label for="rate">Rate:</label>
<input name="rate" type="range" min="0" max="3" value="1" step="0.1">

<label for="pitch">Pitch:</label>

<input name="pitch" type="range" min="0" max="2" step="0.1">
<textarea name="text">Hello! I love JavaScript 👍</textarea>
<button id="stop">Stop!</button>
<button id="speak">Speak</button>

</div>

<script>
const synth = window.speechSynthesis;
let voices = [];
const msg = new SpeechSynthesisUtterance();
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 = '你能说中文吗';

//获得浏览器支持的阅读语言并填充至下拉列表
function getSupportVoice() {
voices = synth.getVoices();
for(i = 0; i < voices.length ; i++) {
var option = document.createElement('option');
option.textContent = voices[i].name + ' (' + voices[i].lang + ')';

if(voices[i].default) {
option.textContent += ' -- DEFAULT';
}

option.setAttribute('data-lang', voices[i].lang);
option.setAttribute('data-name', voices[i].name);
voicesDropdown.appendChild(option);
}
}

//点击speak按钮时阅读文字
function speak() {
console.log(voicesDropdown.value);
synth.speak(msg);
}

//阅读参数发生变化
function paramChange(){
msg[this.name] = this.value;
console.log(this.name,this.value);
}

//停止阅读
function stopSpeak(){
synth.cancel();
}

// 事件绑定
options.forEach(opt => opt.addEventListener('change', paramChange));
synth.addEventListener('voiceschanged', getSupportVoice);//经测试直接执行无效,须由事件触发
speakButton.addEventListener('click', speak);
stopButton.addEventListener('click', stopSpeak);
</script>

</body>
</html>
40 changes: 40 additions & 0 deletions 23 - Speech Synthesis/index-start.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Speech Synthesis</title>
<link rel="stylesheet" href="style.css">
</head>
<body>

<div class="voiceinator">

<h1>The Voiceinator 5000</h1>

<select name="voice" id="voices">
<option value="">Select A Voice</option>
</select>

<label for="rate">Rate:</label>
<input name="rate" type="range" min="0" max="3" value="1" step="0.1">

<label for="pitch">Pitch:</label>

<input name="pitch" type="range" min="0" max="2" step="0.1">
<textarea name="text">Hello! I love JavaScript 👍</textarea>
<button id="stop">Stop!</button>
<button id="speak">Speak</button>

</div>

<script>
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');
</script>

</body>
</html>
98 changes: 98 additions & 0 deletions 23 - Speech Synthesis/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
html {
font-size: 10px;
box-sizing: border-box;
}

*, *:before, *:after {
box-sizing: inherit;
}

body {
margin: 0;
padding: 0;
font-family: sans-serif;
background-color:#3BC1AC;
display:flex;
min-height: 100vh;
align-items: center;

background-image:
radial-gradient(circle at 100% 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent),
radial-gradient(circle at 0 150%, #3BC1AC 24%, #42D2BB 25%, #42D2BB 28%, #3BC1AC 29%, #3BC1AC 36%, #42D2BB 36%, #42D2BB 40%, transparent 40%, transparent),
radial-gradient(circle at 50% 100%, #42D2BB 10%, #3BC1AC 11%, #3BC1AC 23%, #42D2BB 24%, #42D2BB 30%, #3BC1AC 31%, #3BC1AC 43%, #42D2BB 44%, #42D2BB 50%, #3BC1AC 51%, #3BC1AC 63%, #42D2BB 64%, #42D2BB 71%, transparent 71%, transparent),
radial-gradient(circle at 100% 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent),
radial-gradient(circle at 0 50%, #42D2BB 5%, #3BC1AC 6%, #3BC1AC 15%, #42D2BB 16%, #42D2BB 20%, #3BC1AC 21%, #3BC1AC 30%, #42D2BB 31%, #42D2BB 35%, #3BC1AC 36%, #3BC1AC 45%, #42D2BB 46%, #42D2BB 49%, transparent 50%, transparent);
background-size:100px 50px;
}


.voiceinator {
padding:2rem;
width:50rem;
margin:0 auto;
border-radius:1rem;
position: relative;
background:white;
overflow: hidden;
z-index: 1;
box-shadow:0 0 5px 5px rgba(0,0,0,0.1);
}

h1 {
width:calc(100% + 4rem);
margin: -2rem 0 2rem -2rem;
padding:.5rem;
background: #ffc600;
border-bottom: 5px solid #F3C010;
text-align: center;
font-size: 5rem;
font-weight: 100;
font-family: 'Pacifico', cursive;
text-shadow:3px 3px 0 #F3C010;

}

.voiceinator input,
.voiceinator button,
.voiceinator select,
.voiceinator textarea {
width: 100%;
display: block;
margin:10px 0;
padding:10px;
border:0;
font-size: 2rem;
background:#353535;
color:white;
outline:0;
}

textarea {
height: 20rem;
}

input[type="select"] {

}

.voiceinator button {
background:#ffc600;
border:0;
width: 49%;
float:left;
font-family: 'Pacifico', cursive;
margin-bottom: 0;
font-size: 2rem;
border-bottom: 5px solid #F3C010;
cursor:pointer;
position: relative;
}

.voiceinator button:active {
top:2px;
}

.voiceinator button:nth-of-type(1) {
margin-right: 2%;
}

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ No | Guide | Demo
19 | [Webcam Fun 指南](https://github.com/soyaine/JavaScript30/blob/master/19%20-%20Webcam%20Fun/README.md) | [网络摄像头及图片处理在线效果](https://github.com/soyaine/JavaScript30/blob/master/19%20-%20Webcam%20Fun/index-finished-Dashrun.html)
20 | [Speech Detection指南](https://github.com/soyaine/JavaScript30/blob/master/20%20-%20Speech%20Detection/README.md) | [Speech Detection效果](https://github.com/soyaine/JavaScript30/blob/master/20%20-%20Speech%20Detection/index-finished-Dashrun.html)
21 | [Geolocation指南](https://github.com/soyaine/JavaScript30/blob/master/21%20-%20Geolocation/README.md) | [Geolocation效果](https://github.com/soyaine/JavaScript30/blob/master/21%20-%20Geolocation/index-finished-Dashrun.html)
22 | Follow Along Link Highlighter | -
22 | [Follow Along Link Highlighter指南](https://github.com/soyaine/JavaScript30/blob/master/22%20-%20Follow%20Along%20Link%20Highlighter/README.md) | [Follow Along Link Highlighter效果](https://github.com/soyaine/JavaScript30/blob/master/22%20-%20Follow%20Along%20Link%20Highlighter/index-finished-Dashrun.html)
23 | Speech Synthesis | -
24 | Sticky Nav | -
25 | Event Capture, Propagation, Bubbling, and Once | -
Expand All @@ -80,7 +80,7 @@ Name | Contribution
[@DrakeXiang](https://github.com/DrakeXiang) | No.[11](https://github.com/soyaine/JavaScript30/tree/master/11%20-%20Custom%20Video%20Player)
[@zzh466](http://github.com/zzh466) | Review
[@Xing Liu](https://github.com/S1ngS1ng) | Review
[@大史快跑Dashrun](https://github.com/dashrun) | No.[16](https://github.com/soyaine/JavaScript30/tree/master/16%20-%20Mouse%20Move%20Shadow).[17](https://github.com/soyaine/JavaScript30/tree/master/17%20-%20Sort%20Without%20Articles).[18](https://github.com/soyaine/JavaScript30/tree/master/18%20-%20AddingUpTimesWithReduce).[19](https://github.com/soyaine/JavaScript30/blob/master/19%20-%20Webcam%20Fun).[20](https://github.com/soyaine/JavaScript30/tree/master/20%20-%20Speech%20Detection).[21](https://github.com/soyaine/JavaScript30/tree/master/21%20-%20Geolocation)
[@大史快跑Dashrun](https://github.com/dashrun) | No.[16](https://github.com/soyaine/JavaScript30/tree/master/16%20-%20Mouse%20Move%20Shadow).[17](https://github.com/soyaine/JavaScript30/tree/master/17%20-%20Sort%20Without%20Articles).[18](https://github.com/soyaine/JavaScript30/tree/master/18%20-%20AddingUpTimesWithReduce).[19](https://github.com/soyaine/JavaScript30/blob/master/19%20-%20Webcam%20Fun).[20](https://github.com/soyaine/JavaScript30/tree/master/20%20-%20Speech%20Detection).[21](https://github.com/soyaine/JavaScript30/tree/master/21%20-%20Geolocation).[22](https://github.com/soyaine/JavaScript30/tree/master/22%20-%20Follow%20Along%20Link%20Highlighter)
[@缉熙Soyaine](https://github.com/soyaine) | No.[1](https://github.com/soyaine/JavaScript30/tree/master/01%20-%20JavaScript%20Drum%20Kit).[2](https://github.com/soyaine/JavaScript30/tree/master/02%20-%20JS%20%2B%20CSS%20Clock).[3](https://github.com/soyaine/JavaScript30/tree/master/03%20-%20CSS%20%Variables).[4](https://github.com/soyaine/JavaScript30/tree/master/04%20-%20Array%20Cardio%20Day%201).[5](https://github.com/soyaine/JavaScript30/blob/master/05%20-%20Flex%20Panel%20Gallery).[6](https://github.com/soyaine/JavaScript30/blob/master/06%20-%20Type%20Ahead).[7](https://github.com/soyaine/JavaScript30/tree/master/07%20-%20Array%20Cardio%20Day%202).[8](https://github.com/soyaine/JavaScript30/tree/master/08%20-%20Fun%20with%20HTML5%20Canvas).[9](https://github.com/soyaine/JavaScript30/blob/master/09%20-%20Dev%20Tools%20Domination).[10](https://github.com/soyaine/JavaScript30/blob/master/10%20-%20Hold%20Shift%20and%20Check%20Checkboxes/README.md).[12](https://github.com/soyaine/JavaScript30/tree/master/12%20-%20Key%20Sequence%20Detection/README.md).[13](https://github.com/soyaine/JavaScript30/blob/master/13%20-%20Slide%20in%20on%20Scroll/README.md).[14](https://github.com/soyaine/JavaScript30/tree/master/14%20-%20JavaScript%20References%20VS%20Copying)

## JOIN US
Expand Down