iT邦幫忙

2024 iThome 鐵人賽

DAY 15
0
JavaScript

火箭通關JS30系列 第 19

JS30-19 - Webcam Fun

  • 分享至 

  • xImage
  •  

課程目的:

這次的內容是一個基於瀏覽器的影像處理應用,主要功能是從使用者的鏡頭中擷取畫面,並在 HTML <canvas> 元素中套上濾鏡效果並且即時顯示
作品實做

本次功能實作重點:

  • 擷取鏡頭影像
  • 將鏡頭畫面繪製到
  • 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); 用來將像素處理綠幕特效

canvas濾鏡處理

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)

  • 每個像素有4 個數值來表示 RGBA
  • 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"

導讀文件以及學習資源

JS30


上一篇
JS30-18 - Adding Up Times with Reduce
下一篇
JS30-20 - Speech Detection
系列文
火箭通關JS3030
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言