目標 練習操作 Webcam 攝像頭,將攝像頭影像投射到 Video 上,並利用 canvas 來擷取圖片,練習濾鏡的操作。
Demo | Github
處理步驟 前置作業 必須先安裝 node.js ,之後按照以下的設定,設定 package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "name" : "gum" , "version" : "1.0.0" , "description" : "" , "main" : "index.js" , "scripts" : { "start" : "browser-sync start --server --files \"*.css, *.html, *.js\"" }, "author" : "" , "license" : "ISC" , "devDependencies" : { "browser-sync" : "^2.12.5" } }
由於此篇主題需要伺服器模擬,所以使用了 Browsersync ,其啟動方式透過
或是
1 browser-sync start --server --files "*.css, *.html, *.js"
步驟 1. 啟動視訊裝置,並寫入 video tag 呈現出來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 const video = document .querySelector(".player" );const canvas = document .querySelector(".photo" );const ctx = canvas.getContext("2d" );function getVideo ( ) { navigator.mediaDevices .getUserMedia({ video : true , audio : false }) .then(localMediaStream => { video.src = window .URL.createObjectURL(localMediaStream); video.play(); }) .catch(err => { console .error(`OH NO!!!` , err); }); } 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 ); } getVideo(); video.addEventListener("canplay" , paintToCanvas);
參考資料Navigator.mediaDevices MediaDevices.getUserMedia() URL.createObjectURL() canplay CanvasRenderingContext2D.drawImage()
步驟 2. 拍照截圖按鍵觸發功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function takePhoto ( ) { snap.currentTime = 0 ; snap.play(); const data = canvas.toDataURL("image/jpeg" ); const link = document .createElement("a" ); link.href = data; link.setAttribute("download" , "handsome" ); link.innerHTML = `<img src="${data} " alt="Handsome Man" />` ; strip.insertBefore(link, null ); }
參考資料Node.insertBefore()
步驟 3. 添加畫面與色彩分離功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 function rgbSplit (pixels ) { for (let i = 0 ; i < pixels.data.length; i += 4 ) { pixels.data[i - 150 ] = pixels.data[i + 0 ]; pixels.data[i + 500 ] = pixels.data[i + 1 ]; pixels.data[i - 550 ] = pixels.data[i + 2 ]; } return pixels; } 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); let pixels = ctx.getImageData(0 , 0 , width, height); pixels = rgbSplit(pixels); ctx.putImageData(pixels, 0 , 0 ); }, 16 ); }
參考資料getImageData() putImageData()
步驟 4. 添加紅色濾鏡功能
1 2 3 4 5 6 7 8 9 10 11 12 function redEffect (pixels ) { for (let i = 0 ; i < pixels.data.length; i += 4 ) { pixels.data[i + 0 ] = pixels.data[i + 0 ] + 200 ; pixels.data[i + 1 ] = pixels.data[i + 1 ] - 50 ; pixels.data[i + 2 ] = pixels.data[i + 2 ] * 0.5 ; } return pixels; }
步驟 5. 綠幕功能效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function greenScreen (pixels ) { 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 ]; if ( red >= levels.rmin && green >= levels.gmin && blue >= levels.bmin && red <= levels.rmax && green <= levels.gmax && blue <= levels.bmax ) { pixels.data[i + 3 ] = 0 ; } } return pixels; }
筆記與備註事項 HTML 部分 此屬性告知瀏覽器下載這個連結,假如屬性有設定資料,則為下載檔案的預設檔名。
JavaScript 部分 唯讀屬性,取得 MediaDevices 物件,連接視訊裝置,包含攝影鏡頭與麥克風。
MediaDevices.getUserMedia()方法提示用户允许使用一个视频和/或一个音频输入设备,例如相机或屏幕共享和/或麦克风。如果用户给予许可,就返回一个Promise 对象,MediaStream对象作为此Promise对象的Resolved[成功]状态的回调函数参数,相应的,如果用户拒绝了许可,或者没有媒体可用的情况下,PermissionDeniedError 或者NotFoundError作为此Promise的Rejected[失败]状态的回调函数参数。注意,由于用户不会被要求必须作出允许或者拒绝的选择,所以返回的Promise对象可能既不会触发resolve 也不会触发 reject。資料來源 - MDN
靜態方法 URL.createObjectURL() 用於建立一個帶有URL的 DOMString 以代表參數中所傳入的物件. 該URL的生命週期與創造它的window中的 document一致. 這個新的物件URL 代表了所指定的 File 物件 或是 Blob 物件.資料來源 - MDN
播放媒體檔案時候觸發
Canvas 2D API 中的方法,在 Canvas 上繪製圖案
返回一个ImageData对象,用来描述canvas区域隐含的像素数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh。資料來源 - MDN
CanvasRenderingContext2D.putImageData() 是 Canvas 2D API 将数据从已有的 ImageData 对象绘制到位图的方法。 如果提供了一个绘制过的矩形,则只绘制该矩形的像素。此方法不受画布转换矩阵的影响。資料來源 - MDN
在目前元素 ( DOM ) 中的子節點中插入一個子元素
1 2 3 4 5 6 7 8 9 10 var parentElement = document .getElementById('parentElement' );var theFirstChild = parentElement.firstChild;var newElement = document .createElement("div" );parentElement.insertBefore(newElement, theFirstChild);
需要每隔一段時間就重複執行的 function 就可以呼叫此方法。
Canvas 2D API 設定透明度的屬性,設定區間在 0.0 和 1.0 之間。預設值為 1.0
參考資料