Skip to content

Commit c406c61

Browse files
committed
complete day19 readme
1 parent 305ff7c commit c406c61

File tree

1 file changed

+148
-0
lines changed

1 file changed

+148
-0
lines changed

19 - Webcam Fun/README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
> 在Github上看到了[wesbos](https://twitter.com/wesbos)的一个Javascript30天挑战的[repo](https://github.com/wesbos/JavaScript30),旨在使用纯Js来进行练习,不允许使用任何其他的库和框架,该挑战共30天,我会在这里记录下自己练习的过程和遇到的问题。
2+
3+
## Day19 - Webcam Fun
4+
5+
第十九天的练习是使用浏览器的摄像头,实时记录影像,并输出到canvas中,并用canvas对图像进行滤镜的处理。
6+
[线上例子](http://htmlpreview.github.io/?https://github.com/winar-jin/JavaScript30-Challenge/blob/master/19%20-%20Webcam%20Fun/index.html)
7+
> 当你看浏览器查看这个在线例子的时候,你会发现并不能看到页面上出现你的视频画面,打开console面板,你会发现如下提示:
8+
```
9+
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.
10+
```
11+
意思就是只有在安全的连接模式下,才可以使用getUserMedia()的api获取到摄像头的视频信息,那么什么是安全连接呢,主要有HTTPS,localhost,wss,file,chrome-extension等。
12+
更多有关安全连接的信息,请查阅[参考文档](https://www.chromium.org/Home/chromium-security/prefer-secure-origins-for-powerful-new-features).
13+
14+
对于我们的这份例子,我们通过搭建本地localhost服务器,达到安全连接的方式比较方便,因此我们首先收件本地服务器,打开我们项目中的`package.json`文件,会发现里面包含了唯一一个依赖`browser-sync`,可以创建一个本地的localhost服务器,并实时的检测页面文件的变化。(关于browser-sync,更多的可以查阅[参考文档](https://browsersync.io/docs)),使用`npm install`安装browser-sync依赖,安装成功后运行`npm start`即可运行本地localhost服务器,并实时的检测文件的变化,实时刷新。
15+
## 主要思路
16+
* 获取到浏览器的摄像头的影像
17+
* 将影像的记录导出到canvas中
18+
* 通过获取canvas中的图片信息,对图片添加滤镜
19+
20+
## 获取影像
21+
```javascript
22+
function getVideo(){
23+
navigator.mediaDevices.getUserMedia({video:true,audio:false})
24+
.then(videostream => {
25+
console.log(videostream);
26+
video.src = URL.createObjectURL(videostream); // 创建url(creates a URL for the specified object)
27+
video.play();
28+
})
29+
.catch((err) => {
30+
console.error('OH,Don\'t have permission to use your local cam!',err);
31+
});
32+
}
33+
```
34+
* `navigator.mediaDevices.getUserMedia()`方法提示用户允许使用视频或者音频设备,如果用户点击允许,则返回一个Promise对象,MediaStream对象作为此Promise对象的Resolved[成功]状态的回调函数参数;但如果用户点击拒绝或者媒体可以用的时候,同样返回一个Promise对象,且PermissionDeniedError或者NotFoundError作为此Promise的Rejected[失败]状态的回调函数参数。但是,用户也可以直接取消选择,不同意也不拒绝,所以返回的Promise对象可能既不会触发resolve 也不会触发 reject。参数为一个对象,包含要请求的视频和音频情况,布尔类型,请求权限的话为true,vice via。
35+
更详细的内容还请进一步查阅[参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia)
36+
37+
* `URL.createObjectURL()`方法是为了创建一个 DOMString 包含一个表示参数中给定的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示着指定的 File 对象或者 Blob 对象。
38+
(DOMString 是一个UTF-16字符串。由于JavaScript已经使用了这样的字符串,所以DOMString直接映射到一个String。)
39+
更详细的内容请进一步查看[参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL)
40+
41+
## canvas绘图
42+
```javascript
43+
function printToCanvas(){
44+
let width = video.videoWidth;
45+
let height = video.videoHeight;
46+
canvas.height = height;
47+
canvas.width = width; // 勿忘:设置canvas的宽和高
48+
console.log(width,height);
49+
return setInterval(() => {
50+
ctx.drawImage(video,0,0,width,height);
51+
52+
// get the image data
53+
let imagedata = ctx.getImageData(0,0,width,height);
54+
// console.log(imagedata.data);
55+
56+
// mess the image data
57+
// imagedata = redEffect(imagedata);
58+
// imagedata = rgbsplit(imagedata);
59+
// ctx.globalAlpha = 0.2;
60+
imagedata = greenScreen(imagedata);
61+
62+
// put the image data back
63+
ctx.putImageData(imagedata,0,0);
64+
},16);
65+
}
66+
```
67+
* `ctx.drawImage()`更够将当前的视频流(video)中的一帧画在canvas中。
68+
* `ctx.getImageData()`返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。[参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/getImageData)
69+
* `ctx.putImageData()`:该方法是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。 如果提供了脏矩形,只能绘制矩形的像素。 [参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/putImageData)
70+
* imagedata中有大量的数据,其中分别代表了图片的颜色信息,分别为red,green,blue,alpha的值,因此我们可以同添加自定义滤镜,通过改变颜色的rgba的值,控制页面的效果。
71+
72+
## 摄像记录导出到canvas中
73+
```javascript
74+
function takePhoto(){
75+
// 播放音效
76+
snap.currentTime = 0;
77+
snap.play();
78+
79+
// 获取图像数据
80+
let data = canvas.toDataURL('image/jpeg');
81+
// console.log(data);
82+
let link = document.createElement('a');
83+
link.href = data;
84+
link.setAttribute('downlond','handsome');
85+
link.innerHTML = `<img src=${data} alt=handsome>`
86+
strip.insertBefore(link,strip.firstChild);
87+
}
88+
```
89+
* 在没次点击照相的时候,都要求播一遍音效,并且为了模拟现实情况,我们在用户点击时,设置当前的播放时间为0,再播放音效。
90+
* `canvas.toDataURL('image/jpeg');`方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi。 [参考文档](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL)
91+
* 接下来新建一个a元素,设置其href的值为data。在插入在文档中。实现截图成功的效果。
92+
93+
## 自定义滤镜
94+
```javascript
95+
// 红色特效滤镜
96+
function redEffect(imagedata){
97+
for(let i = 0;i<imagedata.data.length;i+=4){
98+
imagedata.data[i + 0] += 200; // red
99+
imagedata.data[i + 1] -= 50; // green
100+
imagedata.data[i + 2] *= 0.5; // blue
101+
}
102+
return imagedata;
103+
}
104+
105+
// RGB分离
106+
function rgbsplit(imagedata){
107+
for(let i = 0;i<imagedata.data.length;i+=4){
108+
imagedata.data[i - 100] = imagedata.data[i + 0]; // red
109+
imagedata.data[i + 150] = imagedata.data[i + 1]; // green
110+
imagedata.data[i - 150] = imagedata.data[i + 2]; // blue
111+
}
112+
return imagedata;
113+
}
114+
115+
// 绿屏(部分消失)
116+
function greenScreen(pixels) {
117+
const levels = {};
118+
119+
document.querySelectorAll('.rgb input').forEach((input) => {
120+
levels[input.name] = input.value;
121+
});
122+
123+
for (i = 0; i < pixels.data.length; i = i + 4) {
124+
red = pixels.data[i + 0];
125+
green = pixels.data[i + 1];
126+
blue = pixels.data[i + 2];
127+
alpha = pixels.data[i + 3];
128+
129+
if (red >= levels.rmin
130+
&& green >= levels.gmin
131+
&& blue >= levels.bmin
132+
&& red <= levels.rmax
133+
&& green <= levels.gmax
134+
&& blue <= levels.bmax) {
135+
// take it out!
136+
pixels.data[i + 3] = 0;
137+
}
138+
}
139+
140+
return pixels;
141+
}
142+
```
143+
这部分主要定义了三个滤镜,由于我们通过`ctx.getImageData`可以获取到页面颜色的rgba的值,,因此我们添加滤镜的原理也是这样,通过循环改变一张图片中的所有rgba的值。就不在具体的聊各个滤镜是怎么实现的了。
144+
145+
## tips
146+
* `debugger`在源程序中添加debugger,可以使程序在运行时,在此处停止,进入调试模式。
147+
148+
OK,这样就可以啦!😀

0 commit comments

Comments
 (0)