這次的內容是一個基於瀏覽器的影像處理應用,主要功能是從使用者的鏡頭中擷取畫面,並在 HTML <canvas>
元素中套上濾鏡效果並且即時顯示
作品實做
const video = document.querySelector(".player");
const canvas = document.querySelector(".photo");
const ctx = canvas.getContext("2d");
const strip = document.querySelector(".strip");
const snap = document.querySelector(".snap");
function getVideo() {
navigator.mediaDevices
.getUserMedia({ video: true, audio: false }) //擷取畫面不擷取聲音
.then((localMediaStream) => {
// console.log(localMediaStream);
video.srcObject = localMediaStream;
video.play();
})
.catch((err) => {
console.error(`出錯了!!`, err);
});
}
getVideo(); //網站開始時執行函式
video.addEventListener("canplay", paintToCanvas);//監測鏡頭活動
navigator.mediaDevices.getUserMedia
瀏覽器會請求使用者的許可來存取攝像頭,返回一個只擷取畫面的MediaStream對象,並且用**video.srcObject
** 賦值**localMediaStream
** 以獲取該元素的媒體源,接著執行video.play()
function paintToCanvas() {
const width = video.videoWidth;
//用來獲取視頻源的原始尺寸。
//例如,如果攝像頭提供的視頻分辨率是 1280x720,video.videoWidth 將是 1280,video.videoH eight 將是 720。
const height = video.videoHeight;
canvas.width = width;
canvas.height = height;
return setInterval(() => {
ctx.drawImage(video, 0, 0, width, height); //座標(0.0)的位置開始繪製
// take the pixels out
let pixels = ctx.getImageData(0, 0, width, height); //此物件拷貝了畫布指定矩形的像素數據
// pixels = blueEffect(pixels);
// pixels = rgbSplit(pixels);
// ctx.globalAlpha = 0.1;
pixels = greenScreen(pixels);
// put them back
ctx.putImageData(pixels, 0, 0);
//透過getImageData() 複製畫布上指定矩形的像素數據,然後透過 putImageData() 將影像資料放回畫布
}, 16);
}
setInterval(()⇒{},16);
每隔 16 毫秒 (約 60 次/秒) 更新一次畫布
let pixels = ctx.getImageData(0, 0, width, height);
從座標 (0, 0) 開始將視頻畫到畫布上,
獲取像素數據,是一個包含所有像素數據的數組,每 4 個元素對應一個像素的 RGBA 值(紅、綠、藍、透明度)
pixels = blueEffect(pixels);
用來將像素處理成藍色特效
pixels = rgbSplit(pixels);
用來將像素做出色板位移的特效
ctx.globalAlpha = 0.1;
是用來設定 canvas 2D 繪圖上下文(ctx
)的全局透明度。畫布上繪製的內容都會有 10% 的不透明度(或 90% 的透明度)
pixels = greenScreen(pixels);
用來將像素處理綠幕特效
function blueEffect(pixels) {
for (i = 0; i < pixels.data.length; i += 4) {
pixels.data[i + 0] = pixels.data[i + 0] * 0.5; //紅色
pixels.data[i + 1] = pixels.data[i + 1] + 50; //綠色
pixels.data[i + 2] = pixels.data[i + 2] + 200; //藍色
}
return pixels;
}
function rgbSplit(pixels) {
for (i = 0; i < pixels.data.length; i += 4) {
pixels.data[i - 150] = pixels.data[i + 0]; //紅色
pixels.data[i + 250] = pixels.data[i + 1]; //綠色
pixels.data[i - 500] = pixels.data[i + 2]; //藍色
}
return pixels;
}
//去被
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;
}
pixels.data[i]
是一個包含所有畫布像素的數組,每 4 個數值對應一個像素的顏色和透明度資訊。
pixels.data[i]
是像素的紅色通道值 (R)。pixels.data[i + 1]
是像素的綠色通道值 (G)。pixels.data[i + 2]
是像素的藍色通道值 (B)。pixels.data[i + 3]
是像素的透明度值 (A)(取值範圍是 0-255,0 是完全透明,255 是完全不透明)。for (i = 0; i < pixels.data.length; i += 4)
i += 4
意思是我們在每次迴圈處理一個完整的像素,通過 pixels.data[i]
、pixels.data[i + 1]
、pixels.data[i + 2]
、pixels.data[i + 3]
來決定該像素的紅、綠、藍、透明度數值。function takePhoto() {
snap.currentTime = 0;
snap.play(); //播放音聲
const data = canvas.toDataURL("image/jpeg");
const link = document.createElement("a");
link.href = data; //將圖片數據URI賦值給 link.href,當用戶點擊該鏈接時,瀏覽器會把它當作一個圖片文件來處理。
link.setAttribute("download", "slime"); //設定屬性為下載
link.innerHTML = `<img src="${data}" alt="slime" />`;
//顯示生成的圖片
strip.insertBefore(link, strip.firstChild);
} //將生成的鏈接插入到頁面的一個容器(strip)中
setAttribute
是用來給標籤設置屬性,在這個例子中,它是用來讓用戶下載一個名為 "slime" 的文件。setAttribute
方法會給該元素設置**download
**:為 <a>
標籤的一個特殊屬性,當用戶點擊該鏈接時,它會觸發文件下載,而不是打開該文件
download
屬性的值將用作下載文件的默認文件名
slime
為下載時設定文件的名稱,瀏覽器會將下載的文件命名為 "slime"