8
8
最后更新:2017-09-02
9
9
10
10
## 挑战任务
11
- 在` index-start.html ` 中提供了一个名为** Take Photo** 的按钮,该按钮的点击事件会触发` takePhoto() ` 函数,并提供了一组标有RGBmin/max标记的` range ` 类型` input ` 元素,一个` canvas ` 元素,一个` video ` 元素,以及带有` strip ` 类名的空` div ` 元素。
12
- 本次的编程任务:
13
-
14
- 1.通过编写javascript代码,请求调用用户的网络摄像头;<br >
15
- 2.在页面上展示来自webcam的数据流信息;<br >
16
- 3.并允许用户保存展示的照片;<br >
17
- 4.及使用滑块来改变图像的色彩。
11
+ 在` index-start.html ` 中提供了一个名为** Take Photo** 的按钮,该按钮的点击事件会触发` takePhoto() ` 函数,并提供了一组标有RGBmin/max标记的` range ` 类型` input ` 元素,一个` canvas ` 元素,一个` video ` 元素,以及带有` strip ` 类名的空` div ` 元素。
12
+ 本次的编程任务:
13
+ 1.通过编写javascript代码,请求调用用户的网络摄像头;
14
+ 2.在页面上展示来自webcam的数据流信息;
15
+ 3.并允许用户保存展示的照片;
16
+ 4.及使用滑块来改变图像的色彩。
18
17
19
18
## 实现效果
20
19
![ 结果展示] ( https://github.com/dashrun/vanilla-javascript-30/blob/master/19%20-%20Webcam%20Fun/effects.png )
21
20
22
21
## 相关知识
23
- 1.` window.navigator ` 对象
24
- ` window.navigator ` 对象上有很多有趣的属性和方法,通过调用相应的方法可以查看到有关当前脚本运行环境(多为浏览器)的相关信息,并使用一些扩展功能。对此不熟悉的开发者可以浏览[ MDN] ( https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator ) 中相关API的介绍,例如使用` getBattery() ` 可以获取电池状态。
25
-
26
- 2.` navigator.getUserMedia ` 方法
27
- ` getUserMedia ` 方法为我们提供了访问网络摄像头或麦克风的权限,该方法接受一个对象作为参数,通过该对象即可获得来自多媒体设备的数据。
28
-
29
- 3.` canvas ` 标签
30
- HTML5强大的扩展功能之一,提供了丰富的图像绘制方法,也是HTML可以作为游戏开发工具的原因之一,本次开发中使用` canvas.getContext('2d') ` 提供的图像操作方法.
31
-
32
- 4.像素数组
33
- 使用` canvas ` 绘图上下文中的` getImageData() ` 获得的像素信息为一个定型数组,每个像素点的rgba色彩信息分别存放在数组中,故数据的格式为:[ 第一像素r值,第一像素g值,第一像素b值,第一像素a值,第二像素r值,第二像素g值......] ,通过各类函数公式对像素数据进行处理即可获得各类不同的滤镜效果。
22
+ 1.` window.navigator ` 对象
23
+ ` window.navigator ` 对象上有很多有趣的属性和方法,通过调用相应的方法可以查看到有关当前脚本运行环境(多为浏览器)的相关信息,并使用一些扩展功能。对此不熟悉的开发者可以浏览[ MDN] ( https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator ) 中相关API的介绍,例如使用` getBattery() ` 可以获取电池状态。
24
+ 2.` navigator.getUserMedia ` 方法
25
+ ` getUserMedia ` 方法为我们提供了访问网络摄像头或麦克风的权限,该方法接受一个对象作为参数,通过该对象即可获得来自多媒体设备的数据。
26
+ 3.` canvas ` 标签
27
+ HTML5强大的扩展功能之一,提供了丰富的图像绘制方法,也是HTML可以作为游戏开发工具的原因之一,本次开发中使用` canvas.getContext('2d') ` 提供的图像操作方法.
28
+ 4.像素数组
29
+ 使用` canvas ` 绘图上下文中的` getImageData() ` 获得的像素信息为一个定型数组,每个像素点的rgba色彩信息分别存放在数组中,故数据的格式为:[ 第一像素r值,第一像素g值,第一像素b值,第一像素a值,第二像素r值,第二像素g值......] ,通过各类函数公式对像素数据进行处理即可获得各类不同的滤镜效果。
34
30
35
31
## 基本思路
36
32
37
- 1.调用` navigator.getUserMedia() ` 方法,若调用成功则返回数据流,若调用失败则在控制台打印相关信息;
38
-
39
- 2.成功调用网络摄像头后,将返回的数据对象绑定至video标签的srcObject属性(注意此处getUserMedia()方法成功调用时触发的回调函数中会传递一个` stream ` 对象,该对象直接赋值给video.src是没有作用的),并当流数据开始传递时,视频自动播放;
40
-
41
- 3.点击` takePhoto() ` 函数时调用` canvas ` 绘图上下文中的` drawImage() ` 方法将视频中当前帧的图像绘制在canvas上,该方法第一个参数可以为图像或视频,其余参数与绘图区域尺寸相关(该方法有多种调用模式,感兴趣的读者可自行学习);
42
-
43
- 4.滤色:在全局中保存滤色范围的上下限,每次滑块数据发生改变后,使用` canvas ` 绘图上下文中的` getImageData() ` 获得画布上指定区域内各像素点的颜色数据,数据被保存在返回对象的data属性中,通过遍历修改像素色彩数组中的数据改变图像的表现,修改后调用` putImageData() ` 方法将像素点重新绘制在` canvas ` 上。
44
-
45
- 5.点击` savePhoto() ` 函数时调用` canvas ` 的` toDataUrl() ` 方法即可获得canvas中的图像数据,默认格式为png,也可修改为其他格式,生成的图像数据指定给` img.src ` 时即可预览图片;
46
-
47
- 6.在` img ` 标签外添加` a ` 标签,并为其添加` download ` 属性,当点击链接时,即可将生成的图片保存至本地。
33
+ 1.调用` navigator.getUserMedia() ` 方法,若调用成功则返回数据流,若调用失败则在控制台打印相关信息;
34
+ 2.成功调用网络摄像头后,将返回的数据对象绑定至video标签的srcObject属性(注意此处getUserMedia()方法成功调用时触发的回调函数中会传递一个` stream ` 对象,该对象直接赋值给video.src是没有作用的),并当流数据开始传递时,视频自动播放;
35
+ 3.点击` takePhoto() ` 函数时调用` canvas ` 绘图上下文中的` drawImage() ` 方法将视频中当前帧的图像绘制在canvas上,该方法第一个参数可以为图像或视频,其余参数与绘图区域尺寸相关(该方法有多种调用模式,感兴趣的读者可自行学习);
36
+ 4.滤色:在全局中保存滤色范围的上下限,每次滑块数据发生改变后,使用` canvas ` 绘图上下文中的` getImageData() ` 获得画布上指定区域内各像素点的颜色数据,数据被保存在返回对象的data属性中,通过遍历修改像素色彩数组中的数据改变图像的表现,修改后调用` putImageData() ` 方法将像素点重新绘制在` canvas ` 上。
37
+ 5.点击` savePhoto() ` 函数时调用` canvas ` 的` toDataUrl() ` 方法即可获得canvas中的图像数据,默认格式为png,也可修改为其他格式,生成的图像数据指定给` img.src ` 时即可预览图片;
38
+ 6.在` img ` 标签外添加` a ` 标签,并为其添加` download ` 属性,当点击链接时,即可将生成的图片保存至本地。
48
39
49
40
## 过程指南
50
- 1.申请调用WebCam
41
+ 1.申请调用WebCam
51
42
``` js
52
43
function askWebcam () {
53
44
navigator .getUserMedia = navigator .getUserMedia ||
@@ -73,52 +64,43 @@ function askWebcam() {
73
64
console .log (' this navigator doesn\' t support webcam!' );
74
65
}
75
66
}
76
- ```
77
-
78
- 2.截图函数 ` takePhoto() ` ,此处将原始图像数据保留一份,否则使用滤色函数处理后,被滤掉的颜色无法恢复,保存了原始图像数据后,只需要重新绘制在canvas上即可。
67
+ ```
68
+ 2.截图函数 ` takePhoto() ` ,此处将原始图像数据保留一份,否则使用滤色函数处理后,被滤掉的颜色无法恢复,保存了原始图像数据后,只需要重新绘制在canvas上即可。
79
69
``` js
80
70
function takePhoto () {
81
71
ctx .drawImage (video, 0 , 0 , 300 , 200 );
82
72
// 将原始截图保存,
83
73
origindata = ctx .getImageData (0 ,0 ,300 ,200 );
84
74
}
85
- ```
86
-
87
- 3.色彩过滤
75
+ ```
76
+ 3.色彩过滤
88
77
在所有滑块的父元素上监听` change ` 事件,当滑块的值发生改变时,先通过e.target.name确定是哪个颜色的范围要求发生了变化,再访问e.target.value获得变化值,通过与全局变量` filter ` 作比较来获得滤色值的上下限要求。
89
- 重点难点解释如下:
78
+ 重点难点解释如下:
90
79
``` js
91
80
/* startPos表示操作像素点数据时的起点,从canvas获取到的像素数据每四个值表示一个像素点例如滑块为红色,则只需要改变像素数组中第0,4,8......个元素的值。通过target.value的首字母即可判断滤色过程应该检查的颜色*/
92
81
const startPos = {' r' : 0 ,' g' : 1 ,' b' : 2 }[target .name [0 ]];
93
82
/* filterMin和filterMax表示相应的滤色范围上下限,若修改了红色滤色范围则取红色范围值。若修改蓝色的滤色范围,则取蓝色。checkFilter()函数将改变后的值与滤色标准`filter`进行比较,将更改滤色标准后需要调整的颜色类别(r,g,b)对应的上下限返回给结果。*/
94
83
var tempFilter = checkFilter (target .name , target .value );
95
84
const filterMin = tempFilter .min ;
96
85
const filterMax = tempFilter .max ;
97
- ```
98
-
99
- 4.保存图片
100
- 使用` canvas.toDataUrl() ` 方法将` canvas ` 画布保存为图片,默认为png格式,该数据可作为` img.src ` 的值,也可利用` a ` 标签将其下载.
86
+ ```
87
+ 4.保存图片
88
+ 使用` canvas.toDataUrl() ` 方法将` canvas ` 画布保存为图片,默认为png格式,该数据可作为` img.src ` 的值,也可利用` a ` 标签将其下载.
101
89
``` js
102
90
// 保存图片
103
91
function savePhoto () {
104
92
img .src = canvas .toDataURL ();
105
93
a .href = canvas .toDataURL ();
106
94
a .setAttribute (' download' , ' handsome' );
107
95
}
108
- ```
109
-
110
- ## 延伸思考
111
- 如何制作一个简易的钢铁侠面板
112
-
113
- 1.本次的编程任务中我们已经可以调用网络摄像头获得视频数据;
114
-
115
- 2.将webcam获取到的数据绘制在canvas中时,同时将可视化面板的UI绘制在canvas中
116
-
117
- 3.视频的本质是连续播放的图片,当图片播放的速度超过每秒24张时,我们将看到连续的动画,当我们以相应的频率重绘canvas上的图像时,即可看到合成后的视频;
118
-
119
- 4.如果要获得更加逼真的效果,可以做更多的图像识别方面的扩展延伸,本篇只做抛砖引玉,不再做更深入的探究。
120
-
121
- 伪代码片段:
96
+ ```
97
+ ## 延伸思考
98
+ ####如何制作一个简易的钢铁侠面板
99
+ 1.本次的编程任务中我们已经可以调用网络摄像头获得视频数据;
100
+ 2.将webcam获取到的数据绘制在canvas中时,同时将可视化面板的UI绘制在canvas中;
101
+ 3.视频的本质是连续播放的图片,当图片播放的速度超过每秒24张时,我们将看到连续的动画,当我们以相应的频率重绘canvas上的图像时,即可看到合成后的视频;
102
+ 4.如果要获得更加逼真的效果,可以做更多的图像识别方面的扩展延伸,本篇只做抛砖引玉,不再做更深入的探究。
103
+ 伪代码片段:
122
104
``` js
123
105
setInterval (drawImg (),1000 / 24 );
124
106
function drawImg (){
0 commit comments