# [JS30] Day.19 Unreal Webcam Fun
###### tags: `JS30`
## 任務 Task
控制鏡頭相機顯示在 `canvas` 上,並能套上濾鏡呈現效果。
==完成時間:2hr==
## 步驟 Step
1. 使用瀏覽器物件(BOM)的 `navigator` 取得 `mediaDevice` 。
2. 再 `getUserMedia` 取得 `promise` 物件,丟給 `video.srcObject`,就可以播放螢幕畫面。
```javascript=
navigator.mediaDevices
.getUserMedia({ video: true, audio: false })//只取螢幕畫面,而不取聲音
.then((localMediaStream) => {
video.srcObject = localMediaStream;
video.play();
})
.catch((err) => {
console.error("OH NO", err);
});
```
3. 建立一個 `paintToCanvas function` 設定畫布尺寸同影像尺寸。
4. 將影像用 `ctx.drawImage` 的方式渲染到畫布上,但因為是圖片,所以需要包在時間函數 `setInterval` 每隔 16 毫秒重新渲染,就會呈現出影片。
```javascript=+
function paintToCanvas() {
const width = video.videoWidth;
const height = video.videoHeight;
canvas.width = width;
canvas.height = height;
return setInterval(() => {
ctx.drawImage(video, 0, 0, width, height);
}, 16);
}
```
5. 監聽影像當可以播放時,就執行 `paintToCanvas()`
```javascript=
video.addEventListener("canplay", paintToCanvas);
```
6. 建立 `takePhoto function` 可以擷取影像並下載。
7. 使用 `canvas.toDataURL` 取得影像網址,放入 `anchor tag href` 裡,並在裡面放入擷取的圖片。
```javascript=
function takePhoto() {
snap.currentTime = 0;
snap.play();
const data = canvas.toDataURL();
const link = document.createElement("a");
link.href = data;
// link.setAttribute("download", "handsome");
link.download = "he";
link.innerHTML = `<img src="${data}" alt="Handsome Man" />`;
strip.appendChild(link);
}
```
8. 濾鏡效果,是讀取每個螢幕像素的顏色,去做更改調色。
```javascript=
let pixels = ctx.getImageData(0, 0, width, height);//取得每個 pixel 的 rgba
function redEffect(pixels) {
for (let i = 0; i < pixels.data.length; i += 4) {
pixels.data[i + 0] = pixels.data[i + 0] + 10;
pixels.data[i + 1] = pixels.data[i + 1] - 50;
pixels.data[i + 2] = pixels.data[i + 2] * 0.5;
}
return pixels;
}
//更改每個像素的 rgba 呈現不同顏色的效果。
```
9. 建立 `greenScreen function` 呈現去背效果。
10. 概念是當像素顏色介於制定的 `max` 跟 `min` 之間時,就將透明度變成 `0` ,就可把該像素顏色去掉。
## 筆記 Note
### <font color=#337EA9>JS Navigator.mediaDevices</font>
* 語法:
```javascript=
navigator.mediaDevices
.getUserMedia(/** setting media constraints */)
.then(/** handle Success */)
.catch(/** handle Error */)
```
* `Navigator` 允許讀取用戶的瀏覽器資料,包含:設定語言、讀取音訊視訊、當前的座標、用電量等等。
* `navigator.mediaDevices.getUserMedia` 回傳的是 `promise` 物件,可以使用 `then` 及 `catch` 來處理。
### <font color=#337EA9>JS debugger</font>
* 建立斷點,進行除錯,但不影響後面程式碼的執行。
* 當想檢查特定程式碼時可以透過斷點去檢查,在此專案則是利用 `debugger;` 去讓 `console.log` 不要一直跑,是個很方便的偵錯語法。
### <font color=#337EA9>16毫秒</font>
* 在設定 `setInterval()` 時,是設定成 16 毫秒,原因在於畫面的螢幕更新率。
* 基本上從 `60Hz` `120Hz` `144Hz` 甚至到 `300Hz`都有,而在控制台>顯示器>進階裡就可以看到螢幕的頻率。

可以看到 ==重新整理頻率(Hz)== 為 60.052Hz,意思是每秒更新 60 次,也就說更新一次是 1000(ms) / 60 = 16.6666 (ms),也就是說 16 毫秒是最快的螢幕渲染速度了,再更低也不會讓畫面更順暢了。
### <font color=#337EA9>去背</font>
* 一般在拍電影的時候使用的綠幕去背,也是用一樣的道理,將像素內含綠色的像素設成透明的,就可去背了。
* 之所以都是用綠色,是因為綠色跟皮膚的顏色相差最遠,如果使用紅色很容易把人也去掉,而藍幕也是有人在用,不過因為藍色比起綠色所使用的機會較高,所以現在大部分都使用綠幕較多。