JS30 Day 19 - Webcam Fun学习笔记

今天这个章节主要是利用电脑的视讯镜头,以canvas为基底,去改变我们的画面色彩、透明度等等。

而我就依照比较重要的重点来分享。

canplay:当影片播放时,触发这个事件。

// 监听是否video正在播放('canplay')video.addEventListener('canplay', paintToCanvas);

navigator.mediaDevices.getUserMedia(constraints):

navigator.mediaDevices可用来串联与麦克风、摄影机或共享萤幕的连结。
getUserMedia()可以用来显示是否允许开启连结至其设备。
getUserMedia(constraints)的constraints参数有2个,分别为video和audio,此处我们只需要影片不需要音档
getUserMedia()会返回一个Promise物件,我们需要利用.then来取得其返回的MediaStream物件

取得MediaStream物件后,我们去处理video的来源,由于作者提示说Chorme、firefox已经不支援video.src = window.URL.createObjectURL(localMediaStream)这个方法,而是改採video.srcObject = localMediaStream;,但由于还要顾及其他浏览器,如IE因此我们利用try、catch来写兼容写法。

// 获取video并开始播放function getVideo() {  // 要video但不要audio  // MediaDevices.getUserMedia()  // 并且会提示说是否要开启视讯镜头  navigator.mediaDevices.getUserMedia({      video: true,      audio: false    })    .then(localMediaStream => {      console.log(localMediaStream);      // MediaStream      // active: true      // id: "sbREnyqNcRJEaadztxfa2cbVz38AnbBmIyiJ"      // onactive: null      // onaddtrack: null      // oninactive: null      // onremovetrack: null      try {        video.srcObject = localMediaStream;       } catch (err) {        console.error(`OH NO!!!`, err);        video.src = window.URL.createObjectURL(localMediaStream);      }      // 原始画面      video.play();    })}

HTMLCanvasElement.getContext(): 返回canvas 的上下文,如果上下文没有定义则返回 null。

利用上下文创建一个二维的画布。

const canvas = document.querySelector('.photo');// ctx为我们所要画的对象const ctx = canvas.getContext('2d');

CanvasRenderingContext2D.drawImage():将图像,画布或视频绘製到画布上。

void ctx.drawImage(image, dx, dy, dWidth, dHeight);

image:放canvas的图像来源。
dx, dy:起始位置(x,y)。
dWidth, dHeight:设定宽高。

CanvasRenderingContext2D.getImageData():取出相片每个像素rgba,且会返回一个ImageData物件

ImageData ctx.getImageData(sx, sy, sw, sh);

sx:複製左上角的x座标。
sy:複製左上角的x座标。
sw:複製的宽度。
sh:複製的高度。

ctx.putImageData(imageData, dx, dy): 把某块区域的像素值呈现在指定的位址上

getImageData 跟putImageData是一组的,它可以让你取得每个像素的rgba值然后作变化放回去

此处我们设置一个名为pixels的变数,来观察普通面积与pixels的差别,一个点会被拆分成rgba,也就是pixels为area的四倍,可观察出面积与点的关係会差四倍,会看到我们储存像素点的地方是一个叫做Uint8ClampedArray的array,其内共有1228800个数值所组成,4个为一组,表示一个rgba的像素点要呈现的颜色

let pixels = ctx.getImageData(0, 0, width, height);  console.log(pixels); ImageData {data: Uint8ClampedArray(1228800), width: 640, height: 480}  console.log(`Area: ${width * height},Pixels ${pixels.data.length}`); Area: 307200,Pixels 1228800  console.log(pixels.data[0], pixels.data[1], pixels.data[2], pixels.data[3]);  // 106 114 55 255  console.log(pixels.data[0 + 4], pixels.data[1 + 4], pixels.data[2 + 4], pixels.data[3 + 4]);  // 109 116 57 255

因为影片会一直更新,所以设置计时器让他一直跑,我们所做的视讯效果也是在这边呼叫。

  return setInterval(() => {    // 画一个video,从起始位置(0,0),画width,height的宽高    ctx.drawImage(video, 0, 0, width, height);    // take the pixels out    let pixels = ctx.getImageData(0, 0, width, height);    // mess with them    // pixels = redEffect(pixels);    // console.log(pixels.data[0], pixels.data[1], pixels.data[2], pixels.data[3]);    // pixels = rgbSplit(pixels);    // ctx.globalAlpha = 0.8;    pixels = greenScreen(pixels);    // put them back    ctx.putImageData(pixels, 0, 0);  }, 16);

insertBefore(newnode,existingnode): 在特定的 DOM 元素前面插入新的子节点。

strip.insertBefore(link, strip.firstChild),在父元素下,将每次新产的link插入至第一个子元素的最前面

download: < a download="filename" >,为a标籤的一个属性,当点击其元素就会跳出另存新档

canvas.toDataURL(type, encoderOptions): 这个语法可以把图片转成 base64,回传含有图像和参数设置特定格式的 data URIs (预设 PNG).,回传的图像解析度为 96 dpi。

type:图像格式,预设为 image/png.,表示 image/jpeg或是image/webp的图像品质,不加就为预设。

此处为拍照功能的部分。

function takePhoto() {  // played the sound  snap.currentTime = 0;  snap.play();  // 做截图的功能  // canvas.toDataURL(type, encoderOptions);  // type:图像格式  预设为 image/png. 表示 image/jpeg 或是 image/webp 的图像品质 不加就为预设。   // 回传含有图像和参数设置特定格式的 data URIs (预设 PNG). 回传的图像解析度为 96 dpi  const data = canvas.toDataURL('image/jpeg');  const link = document.createElement('a');  link.href = data;  // 创建一个download属性,其预设值为handsome,当点击其元素就会跳出另存新档  link.setAttribute('download', 'handsome');  // 将图加至link中  link.innerHTML = `<img src="${data}" alt="Handsome Man" />`;  // Node.insertBefore() 方法将一个节点插入至参考节点之前  // parentNode.insertBefore(newNode, referenceNode);  // 将每次新产的link插入至第一个子元素的最前面  strip.insertBefore(link, strip.firstChild);}

以下几种功能,主要都是以四个色板rgba对应0,1,2,3下去做调整,i+=4的原因是因为rgba四个为一组,所以每次跳四个才会到下一个rgba。

红色效果

将r部分的颜色提高,其余减少。

function redEffect(pixels) {  // console.log(pixels.data.length); // 1228800  for (let i = 0; i < pixels.data.length; i += 4) {    // 在什么位置对其做调色    pixels.data[i + 0] = pixels.data[i + 0] + 200; // RED    pixels.data[i + 1] = pixels.data[i + 1] - 50; // GREEN    pixels.data[i + 2] = pixels.data[i + 2] * 0.5; // Blue  }  return pixels;}

色彩分离

将rgb不同的颜色移动到不同的位置即可。

function rgbSplit(pixels) {  for (let i = 0; i < pixels.data.length; i += 4) {    // 固定的颜色对不同位置去做变化(渐层)    pixels.data[i - canvas.width * 4 * 50] = pixels.data[i + 0]; // RED    pixels.data[i - canvas.width * 4 * 30] = pixels.data[i + 1]; // GREEN    pixels.data[i - canvas.width * 4 * 10] = pixels.data[i + 2]; // Blue  }  return pixels;}

过滤去背

只要颜色落在指定区间,就让他变透明(a变为0)。

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) {      // 将alpha设为0      pixels.data[i + 3] = 0;    }  }  return pixels;}

retro风格

加重黄红比例即可。

function oldStyle(pixels) {    for (let i = 0; i < pixels.data.length; i += 4) {        pixels.data[i + 0] += 150; //red        pixels.data[i + 1] += 60; //green        pixels.data[i + 2] += 60; //blue        pixels.data[i + 3] *= 0.8; //alpha    }    return pixels;}

负片效果

将颜色变成互补色,利用255去减掉原本取得的颜色即可。

function negativeEffect(pixels) {  for (let i = 0; i < pixels.data.length; i += 4) {    pixels.data[i + 0] = 255 - pixels.data[i + 0]; //red    pixels.data[i + 1] = 255 - pixels.data[i + 1]; //green    pixels.data[i + 2] = 255 - pixels.data[i + 2]; //blue  }  return pixels;}

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章