使用JavaScript和React访问网络摄像头
我不喜欢垃圾(恶意软件)的网络摄像头软件。
2020年秋季时,我购买了一个新Microsoft LifeCam,当我得知必须下载他们的官方软件才能使用时,立即就产生了反感。我也尝试了其他软件,但事实让人非常无语,我对自己说:“用JavaScript连接到我的网络摄像头有多难?”
实际上,这的确很容易。你可以创建自己的浏览器应用程序来拍照、录视频甚至创建滤镜。这完全取决于你的想象力和创造力。
在本文中,我们将讨论这一点。
第一步
首先,我们需要创建HTML部分。我想拍照,于是添加了一个按钮。我们将在一分钟内为所有这些样式进行设置和修改。
import React from "react";
const App = () => {
return (
<div>
<button>Take a photo</button>
<video/>
</div>
);
};
export default App;
分解上面的代码; button
是用来给自己拍照, video
用于我们的网络摄像头流,而canvas是用于从视频中拍摄快照并将其显示出来。
下一步是访问我们的网络摄像头。为此,我们需要使用Hooks的useRef功能。具体步骤如下,我们需要添加:
ref={videoRef}
到所需的HTML元素,然后从我们的组件顶部使用
const videoRef = useRef(null);
useRef是使用React访问DOM元素的方法。它的作用是创建一个普通的JavaScript对象,该对象包含一些属性(如current),这对于内部保留任何可变值时很有用。与纯对象的不同之处在于,它在每个渲染上都为你提供相同的ref对象。
接下来要做的就是创建一种获取视频并将其流式传输的方法,
const App = () => {
const videoRef = useRef(null);
const getVideo = () => {
navigator.mediaDevices
.getUserMedia({ video: { width: 300 } })
.then(stream => {
let video = videoRef.current;
video.srcObject = stream;
video.play();
})
.catch(err => {
console.error("error:", err);
});
};
return (
<div>
<button>Take a photo</button>
<video ref={videoRef}/>
<canvas />
</div>
</div>
);
};
export default App;
兴趣点是关键字: navigator
。
直接从MDN读取:
该
Navigator
界面表示用户代理的状态和身份。它允许脚本查询并自己注册以进行一些活动。
Navigator
对象可以使用只读window.navigator
属性
这是不言自明的。但是最好自己看看,只需要打开开发工具并在控制台上键入navigator即可。
仔细观察,里面 mediaDevices
属性。如果对其进行扩展,则可以看到已定义的原型即 getUserMedia, 该原型返回一个Promise。
我们将使用它来访问我们的网络摄像头。再次直接从MDN复制,
navigator.mediaDevices():
返回到 MediaDevices
对象的引用,然后该对象可用于获取有关媒体设备( MediaDevices.enumerateDevices()
)的信息,并使用 MediaDevices.getUserMedia()
来请求对媒体的访问。
在兑现此承诺的部分,我们可以将网络摄像头中的视频分配给我们使用useRef访问的视频标签。
let video = videoRef.current;
video.srcObject = stream;
video.play();
这是我们访问WebCam所需的全部操作。能获得多少乐趣完全取决于你自己!创建滤镜、添加效果、拍照和处理图像数据……有着无限可能!
现在,你的代码应如下所示:
import React, { useEffect, useRef } from "react";
const App = () => {
const videoRef = useRef(null);
useEffect(() => {
getVideo();
}, [videoRef]);
const getVideo = () => {
navigator.mediaDevices
.getUserMedia({ video: { width: 300 } })
.then(stream => {
let video = videoRef.current;
video.srcObject = stream;
video.play();
})
.catch(err => {
console.error("error:", err);
});
};
return (
<div>
<div>
<button>Take a photo</button>
<video ref={videoRef} />
</div>
</div>
);
};
export default App;
第二步
从我们刚刚访问的网络摄像头拍摄快照也很容易。我们所需要做的只是创建一个画布并将数据写入该画布中。这里最重要的是从WebCam获取快照并将其作为2D上下文进行渲染。
然后,以每200毫秒将该2D上下文写入到Canvas中。
const paintToCanvas = () => {
let video = videoRef.current;
let photo = photoRef.current;
let ctx = photo.getContext("2d");
const width = 320;
const height = 240;
photo.width = width;
photo.height = height;
return setInterval(() => {
ctx.drawImage(video, 0, 0, width, height);
}, 200);
};
要停止绘制,我们需要将视频源设置为 null, 并在流中的轨道上使用stop()。这就足够了:
const stop = (e) => {
const stream = video.srcObject;
const tracks = stream.getTracks();
for (let i = 0; i < tracks.length; i++) {
let track = tracks[i];
track.stop();
}
video.srcObject = null;
}
此时,你应该设置网络摄像头在一侧播放,并且画布会随时间间隔进行更新。
react-webcam-step2 - StackBlitz
Starter project for React apps that exports to the create-react-app CLI.
.toDataURL()
要将摄像头快照中的照片拍摄到画布上,我们需要使用 toDataURL
。
用这个方法返回数据URI,其中包含由 type
参数指定的格式表示的图像。默认情况下,它是PNG。Chrome也支持该 image/webp
类型。
canvas.toDataURL('image/jpeg', 1.0);
逗号后的部分用于指定照片的质量(从0到1)。
之后,我们仅需要创建一个图像元素并添加我们拍摄的图片作为源。我还添加了一个属性供我们下载。
link.setAttribute(“event-name”, “name-for-the-image”);
const takePhoto = () => {
let photo = photoRef.current;
let strip = stripRef.current;
const data = photo.toDataURL("image/jpeg");
const link = document.createElement("a");
link.href = data;
link.setAttribute("download", "myWebcam");
link.innerHTML = `<img src='${data}' alt='thumbnail'/>`;
strip.insertBefore(link, strip.firstChild);
};
<button onClick={() => takePhoto()}>Take a photo</button>
在进行下一步之前,以下是代码的外观:
react-webcam-step3 - StackBlitz
导出到create-react-app CLI的React应用的Starter项目.
stackblitz.com
有趣的部分
针对本文,我将添加一项功能来拍照,并使用网络摄像头中的1个像素给我创建的小场景添加效果。该场景是由Cassie Evans启发的
https://codepen.io/cassie-codes/embed/ZjErdL?default-tab=&theme-id=
我们将从快照中获取一个像素并操纵我创建的场景。天空将随着该像素发生相应的变化。
场景
为了创建场景,我检查了freepik并找到了一张有山脉的图像 ( 可以在 这里 找到 ) 。
现在,在 paintToCanvas
函数内部,我们将添加更多行。但是在此之前,我们会将场景添加到我们的HTML部分中。
<div ref={colorRef} className="scene">
<img
className="mountains"
src="https://i.ibb.co/RjYk1Ps/2817290-eps-1.png"
/>
</div>
你看到 ref
用法了吗?同样,借助 useRef
,我们将在稍后为该元素添加一些样式。
要添加这些样式,我将从快照及其颜色值中获取第一个像素。
const paintToCanvas = () => {
let video = videoRef.current;
let photo = photoRef.current;
let ctx = photo.getContext("2d");
const width = 320;
const height = 240;
photo.width = width;
photo.height = height;
return setInterval(() => {
let color = colorRef.current;
ctx.drawImage(video, 0, 0, width, height);
let pixels = ctx.getImageData(0, 0, width, height);
color.style.backgroundColor = `rgb(${pixels.data[0]},${pixels.data[1]},${
pixels.data[2]
})`;
color.style.borderColor = `rgb(${pixels.data[0]},${pixels.data[1]},${
pixels.data[2]
})`;
}, 200);
};
这是最终版本:
https://itnext.io/accessing-the-webcam-with-javascript-and-react-33cbe92f49cb
感谢阅读!特别感谢Semih Onay的审核以及CassieEvans让我使用她的Codepen。快去关注她!
原文作者 Gökhan İpek
原文链接https://itnext.io/accessing-the-webcam-with-javascript-and-react-33cbe92f49cb