Skip to content

Commit

Permalink
update complete v1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
liyuechun committed Aug 14, 2017
1 parent 84696ee commit fa893d7
Show file tree
Hide file tree
Showing 5,235 changed files with 706,042 additions and 34 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
136 changes: 102 additions & 34 deletions 19 - Webcam Fun/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,80 @@
> 在Github上看到了[wesbos](https://twitter.com/wesbos)的一个Javascript30天挑战的[repo](https://github.com/wesbos/JavaScript30),旨在使用纯Js来进行练习,不允许使用任何其他的库和框架,该挑战共30天,我会在这里记录下自己练习的过程和遇到的问题。
# Day19 - 摄像、拍照,滤镜中文指南

## Day19 - Webcam Fun
> 本文出自:[春哥个人博客:http://www.liyuechun.org](http://liyuechun.org)
> 作者:©[黎跃春-追时间的人](http://weibo.com/mobiledevelopment)
> 简介:[JavaScript30](https://javascript30.com)[Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 19 篇。完整中文版指南及视频教程在 [从零到壹全栈部落](http://kongyixueyuan.com/course/4188)
第十九天的练习是使用浏览器的摄像头,实时记录影像,并输出到canvas中,并用canvas对图像进行滤镜的处理。
[线上例子](http://htmlpreview.github.io/?https://github.com/winar-jin/JavaScript30-Challenge/blob/master/19%20-%20Webcam%20Fun/index.html)
> 当你看浏览器查看这个在线例子的时候,你会发现并不能看到页面上出现你的视频画面,打开console面板,你会发现如下提示:

## 效果图

```
getUserMedia() no longer works on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.
```
意思就是只有在安全的连接模式下,才可以使用getUserMedia()的api获取到摄像头的视频信息,那么什么是安全连接呢,主要有HTTPS,localhost,wss,file,chrome-extension等。
更多有关安全连接的信息,请查阅[参考文档](https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features).
![](http://om1c35wrq.bkt.clouddn.com/day19.gif)
![](http://om1c35wrq.bkt.clouddn.com/day19-red%E6%95%88%E6%9E%9C%E5%9B%BE.png)

JS30天第19天挑战的是如何调用摄像头录像、播放,如何捕捉视频将其绘制`canvas`,还有拍照,以及滤镜的制作。


## 运行项目

1. 通过`npm install`安装依赖包
2. 通过`npm start`启动服务器
3. 浏览器直接访问`http://localhost:3000`

```js
liyuechun:19 - Webcam Fun yuechunli$ pwd
/Users/liyuechun/Documents/js30/JavaScript30-liyuechun/19 - Webcam Fun
liyuechun:19 - Webcam Fun yuechunli$ ls
README.md package-lock.json scripts.js
index.html package.json style.css
liyuechun:19 - Webcam Fun yuechunli$ npm install

> fsevents@1.1.2 install /Users/liyuechun/Documents/js30/JavaScript30-liyuechun/19 - Webcam Fun/node_modules/fsevents
> node install

对于我们的这份例子,我们通过搭建本地localhost服务器,达到安全连接的方式比较方便,因此我们首先收件本地服务器,打开我们项目中的`package.json`文件,会发现里面包含了唯一一个依赖`browser-sync`,可以创建一个本地的localhost服务器,并实时的检测页面文件的变化。(关于browser-sync,更多的可以查阅[参考文档](https://browsersync.io/docs)),使用`npm install`安装browser-sync依赖,安装成功后运行`npm start`即可运行本地localhost服务器,并实时的检测文件的变化,实时刷新。
[fsevents] Success: "/Users/liyuechun/Documents/js30/JavaScript30-liyuechun/19 - Webcam Fun/node_modules/fsevents/lib/binding/Release/node-v57-darwin-x64/fse.node" already installed
Pass --update-binary to reinstall or --build-from-source to recompile
npm WARN gum@1.0.0 No repository field.

added 411 packages in 5.921s
liyuechun:19 - Webcam Fun yuechunli$ npm start

> gum@1.0.0 start /Users/liyuechun/Documents/js30/JavaScript30-liyuechun/19 - Webcam Fun
> browser-sync start --server --files '*.css, *.html, *.js'

[Browsersync] Access URLs:
--------------------------------------
Local: http://localhost:3000
External: http://192.168.1.116:3000
--------------------------------------
UI: http://localhost:3001
UI External: http://192.168.1.116:3001
--------------------------------------
[Browsersync] Serving files from: ./
[Browsersync] Watching files...
```

## 主要思路

* 获取到浏览器的摄像头的影像
* 将影像的记录导出到canvas中
* 通过获取canvas中的图片信息,对图片添加滤镜


## Browsersync

#### 项目结构

![](http://om1c35wrq.bkt.clouddn.com/Snip20170809_21.png)

#### 了解Browsersync

省时的浏览器同步测试工具,Browsersync能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。更重要的是`Browsersync可以同时在PC、平板、手机`等设备下进项调试。您可以想象一下:“假设您的桌子上有pc、ipad、iphone、android等设备,同时打开了您需要调试的页面,当您使用browsersync后,您的任何一次代码保存,以上的设备都会同时显示您的改动”。无论您是前端还是后端工程师,使用它将提高您30%的工作效率。

![](http://om1c35wrq.bkt.clouddn.com/sync-demo.gif)

有了它,您不用在多个浏览器、多个设备间来回切换,频繁的刷新页面。更神奇的是您在一个浏览器中滚动页面、点击等行为也会同步到其他浏览器和设备中,这一切还可以通过可视化界面来控制。

![](http://om1c35wrq.bkt.clouddn.com/scroll-demo.gif)

## 获取影像

```javascript
Expand All @@ -35,12 +90,13 @@ function getVideo(){
});
}
```
* `navigator.mediaDevices.getUserMedia()`方法提示用户允许使用视频或者音频设备,如果用户点击允许,则返回一个Promise对象,MediaStream对象作为此Promise对象的Resolved[成功]状态的回调函数参数;但如果用户点击拒绝或者媒体可以用的时候,同样返回一个Promise对象,且PermissionDeniedError或者NotFoundError作为此Promise的Rejected[失败]状态的回调函数参数。但是,用户也可以直接取消选择,不同意也不拒绝,所以返回的Promise对象可能既不会触发resolve 也不会触发 reject。参数为一个对象,包含要请求的视频和音频情况,布尔类型,请求权限的话为true,vice via。
更详细的内容还请进一步查阅[参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia)

* `URL.createObjectURL()`方法是为了创建一个 DOMString 包含一个表示参数中给定的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示着指定的 File 对象或者 Blob 对象。
(DOMString 是一个UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString直接映射到一个String。)
更详细的内容请进一步查看[参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL)
- [MediaDevices.getUserMedia()](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia)
> `MediaDevices.getUserMedia()`方法提示用户允许使用一个视频和/或一个音频输入设备,例如相机或屏幕共享和/或麦克风。如果用户给予许可,就返回一个`Promise`对象,`MediaStream`对象作为此`Promise`对象的`Resolved`[成功]状态的回调函数参数,相应的,如果用户拒绝了许可,或者没有媒体可用的情况下,`PermissionDeniedError`或者`NotFoundError`作为此`Promise``Rejected`[失败]状态的回调函数参数。注意,由于用户不会被要求必须作出允许或者拒绝的选择,所以返回的`Promise`对象可能既不会触发`resolve`也不会触发`reject`

- [URL.createObjectURL()](https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL)
> `URL.createObjectURL()` 静态方法会创建一个`DOMString`,其中包含一个表示参数中给出的对象的`URL`。这个`URL` 的生命周期和创建它的窗口中的`document` 绑定。这个新的`URL`对象表示指定的`File` 对象或`Blob` 对象。
## canvas绘图
```javascript
Expand Down Expand Up @@ -68,10 +124,17 @@ function printToCanvas(){
},16);
}
```
* `ctx.drawImage()`更够将当前的视频流(video)中的一帧画在canvas中。
* `ctx.getImageData()`返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。[参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/getImageData)
* `ctx.putImageData()`:该方法是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。 如果提供了脏矩形,只能绘制矩形的像素。 [参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/putImageData)
* imagedata中有大量的数据,其中分别代表了图片的颜色信息,分别为red,green,blue,alpha的值,因此我们可以同添加自定义滤镜,通过改变颜色的rgba的值,控制页面的效果。
* `ctx.drawImage()`
>它能够将当前的视频流(video)中的一帧画在canvas中。
- [getImageData()](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/getImageData)
> `ctx.getImageData()`返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。
- [putImageData()](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/putImageData)
> `ctx.putImageData()`:该方法是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。 如果提供了脏矩形,只能绘制矩形的像素。
- imagedata信息
> imagedata中有大量的数据,其中分别代表了图片的颜色信息,分别为red,green,blue,alpha的值,因此我们可以同添加自定义滤镜,通过改变颜色的rgba的值,控制页面的效果。
## 摄像记录导出到canvas中

Expand All @@ -91,9 +154,9 @@ function takePhoto(){
strip.insertBefore(link,strip.firstChild);
}
```
* 在没次点击照相的时候,都要求播一遍音效,并且为了模拟现实情况,我们在用户点击时,设置当前的播放时间为0,再播放音效。
* `canvas.toDataURL('image/jpeg');`方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。 [参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL)
* 接下来新建一个a元素,设置其href的值为data。在插入在文档中。实现截图成功的效果。
- 在没次点击照相的时候,都要求播一遍音效,并且为了模拟现实情况,我们在用户点击时,设置当前的播放时间为0,再播放音效。
- [canvas.toDataURL('image/jpeg');](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL)方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。
- 接下来新建一个a元素,设置其href的值为data。在插入在文档中。实现截图成功的效果。

## 自定义滤镜

Expand All @@ -119,18 +182,18 @@ function rgbsplit(imagedata){
}

// 绿屏(部分消失)
function greenScreen(pixels) {
function greenScreen(imagedata) {
const levels = {};

document.querySelectorAll('.rgb input').forEach((input) => {
levels[input.name] = input.value;
});

for (i = 0; i < pixels.data.length; i = i + 4) {
red = pixels.data[i + 0];
green = pixels.data[i + 1];
blue = pixels.data[i + 2];
alpha = pixels.data[i + 3];
red = imagedata.data[i + 0];
green = imagedata.data[i + 1];
blue = imagedata.data[i + 2];
alpha = imagedata.data[i + 3];

if (red >= levels.rmin
&& green >= levels.gmin
Expand All @@ -139,17 +202,22 @@ function greenScreen(pixels) {
&& green <= levels.gmax
&& blue <= levels.bmax) {
// take it out!
pixels.data[i + 3] = 0;
imagedata.data[i + 3] = 0;
}
}

return pixels;
return imagedata;
}
```
这部分主要定义了三个滤镜,由于我们通过`ctx.getImageData`可以获取到页面颜色的rgba的值,,因此我们添加滤镜的原理也是这样,通过循环改变一张图片中的所有rgba的值。就不在具体的聊各个滤镜是怎么实现的了。
这部分主要定义了三个滤镜,由于我们通过`ctx.getImageData`可以获取到页面颜色的rgba的值,因此我们添加滤镜的原理也是这样,通过循环改变一张图片中的所有rgba的值即可。


## 源码下载

[Github Source Code](https://github.com/liyuechun/JavaScript30-liyuechun)

## tips
* `debugger`在源程序中添加debugger,可以使程序在运行时,在此处停止,进入调试模式。
|全栈部落|区块链部落|
|:---------:|:------:|
|![](http://orhm8wuhd.bkt.clouddn.com/quanzhanbuluo100.jpeg)|![](http://orhm8wuhd.bkt.clouddn.com/qukuailian100.jpg)|

OK,这样就可以啦!😀

178 changes: 178 additions & 0 deletions 20 - Speech Detection/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Day20 - 语言识别系统中文指南

> 本文出自:[春哥个人博客:http://www.liyuechun.org](http://liyuechun.org)
> 作者:©[黎跃春-追时间的人](http://weibo.com/mobiledevelopment)
> 简介:[JavaScript30](https://javascript30.com)[Wes Bos](https://github.com/wesbos) 推出的一个 30 天挑战。项目免费提供了 30 个视频教程、30 个挑战的起始文档和 30 个挑战解决方案源代码。目的是帮助人们用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。现在你看到的是这系列指南的第 20 篇。完整中文版指南及视频教程在 [从零到壹全栈部落](http://kongyixueyuan.com/course/4188)
## 运行项目


```js
$ npm install
$ npm start
```

浏览器打开`http://localhost:3000/index-FINISHED.html`

效果图如下:

![](http://om1c35wrq.bkt.clouddn.com/Snip20170811_1.png)

![](http://om1c35wrq.bkt.clouddn.com/day20-100.gif)


## 程序源码

### HTML代码



```js html css
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Speech Detection</title>
</head>

<body>

<div class="words" contenteditable>
</div>

<style>
html {
font-size: 10px;
}

body {
background: #ffc600;
font-family: 'helvetica neue';
font-weight: 200;
font-size: 20px;
}

.words {
max-width: 500px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.1);
padding: 1rem 2rem 1rem 5rem;
background: -webkit-gradient(linear, 0 0, 0 100%, from(#d9eaf3), color-stop(4%, #fff)) 0 4px;
background-size: 100% 3rem;
position: relative;
line-height: 3rem;
}

p {
margin: 0 0 3rem;
}

.words:before {
content: '';
position: absolute;
width: 4px;
top: 0;
left: 30px;
bottom: 0;
border: 1px solid;
border-color: transparent #efe4e4;
}
</style>

</body>

</html>
```

### JS代码

```js
<script>
// 根据浏览器之间的兼容性,需要同时添加浏览器前缀
window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

// 实例化SpeechRecognition对象
const recognition = new SpeechRecognition();

// interimResults 属性的默认值是 false ,代表语音识别器的返回值不会改变。在这个演示中,我们把它设置为 true ,这样随着我们的输入,识别结果有可能会改变。仔细观看演示,灰色的文字是临时性的,有时会改变,而黑色文本是最终结果,不会改变。
recognition.interimResults = true;

// 创建p便签,附加到DOM树中
let p = document.createElement('p');
const words = document.querySelector('.words');
words.appendChild(p);

// 监听recognition的result事件,获取到语音输入的文字
recognition.addEventListener('result', (e) => {
const results = Array.from(e.results) // e.results中保存的是识别的结果,本来并不是数组,需要将其转换为数组,方便使用其map、join等方法。
.map(result => result[0])
.map(result => result.transcript) // 获取到每一段话,是一个数组类型
.join(''); // 将每一段话连接成字符串

// 可以动态的将其中的某一个词语换掉
const poopScript = results.replace(/good/gi, '👍');
p.textContent = poopScript;

// 如果当前一段输入结束了,也就是有停顿,就会新建一个p便签
if (e.results[0].isFinal) {
p = document.createElement('p');
words.appendChild(p);
}
});

// 监听recognition的end事件,当前输入结束后,再次开始,使其一直处于输入状态
recognition.addEventListener('end', recognition.start);

// 开启recognition
recognition.start();
</script>
```

#### JS实现思路
* 新建一个语音识别的对象
* 开启该语音识别对象的识别服务
* 监听`result`事件,实时获取语音输入内容
* 监听`end`事件,当结束时再次开启语音识别,使其持续监听

#### JS源码解析

- [SpeechRecognition参考文档](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition)

- 其中监听`result`事件,根据事件返回值获取到语音输入的内容
![](http://om1c35wrq.bkt.clouddn.com/Snip20170811_2.png)

可以看到`transcript`中保存的是语音输入的内容。其中可以看到还有一个属性为`confidence`,代表这段话是别的精度,越大正确率越高。

-`SpeechRecognition`属性

```js
<!--新建语音识别的对象-->
var recognition = new SpeechRecognition();
<!--continuous默认值为false,当continuous值为true时,表示,一句话结束后,语音识别继续识别-->
recognition.continuous = false;
<!--设置按照什么语言来识别-->
recognition.lang = 'en-US';
<!--interimResults 属性的默认值是 false ,代表语音识别器的返回值不会改变。在这个演示中,我们把它设置为 true ,这样随着我们的输入,识别结果有可能会改变。仔细观看演示,灰色的文字是临时性的,有时会改变,而黑色文本是最终结果,不会改变。-->
recognition.interimResults = false;
...
```

第20天的内容就到这里,主要学习`SpeechRecognition`相关属性的使用。


## 源码下载

[Github Source Code](https://github.com/liyuechun/JavaScript30-liyuechun)

|全栈部落|区块链部落|
|:---------:|:------:|
|![](http://orhm8wuhd.bkt.clouddn.com/quanzhanbuluo100.jpeg)|![](http://orhm8wuhd.bkt.clouddn.com/qukuailian100.jpg)|






Loading

0 comments on commit fa893d7

Please sign in to comment.