React如何实现点击Open Camera按钮唤起摄像头拍照功能
React 实现点击按钮调用摄像头拍照方案
核心依赖浏览器原生MediaDevices.getUserMedia() API实现,无需额外安装第三方依赖,可直接适配你现有的按钮绑定逻辑。
具体实现步骤
- 声明DOM引用和状态:需要2个核心DOM元素,分别是用来实时预览摄像头画面的
<video>标签、用来截取画面生成照片的隐藏<canvas>标签,通过useRef获取元素实例,同时用状态保存摄像头媒体流、拍摄完成的图片数据。 - 编写
openCamera点击逻辑:点击按钮后向用户申请摄像头权限,拿到媒体流后绑定到video元素开启实时预览,同时做好权限拒绝、无设备等异常场景的提示。 - 实现拍照逻辑:点击拍照按钮时,把video当前帧绘制到canvas上,导出为可直接渲染、上传的图片地址。
- 资源清理逻辑:组件卸载、手动关闭摄像头时一定要终止媒体流,避免摄像头被后台持续占用。
可直接复用的完整代码示例
import { useRef, useState, useEffect } from 'react'; import styles from './你的样式文件路径.module.css'; export default function CameraComponent() { // DOM实例引用 const videoRef = useRef(null); const canvasRef = useRef(null); // 页面状态 const [mediaStream, setMediaStream] = useState(null); const [capturedImg, setCapturedImg] = useState(null); const [isCameraOpen, setIsCameraOpen] = useState(false); // 你需要绑定的openCamera核心逻辑 const openCamera = async () => { try { // 申请摄像头权限,获取媒体流 const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user' }, // 传'environment'优先调用后置摄像头 audio: false }); setMediaStream(stream); // 将媒体流绑定到video元素,开启实时预览 if (videoRef.current) { videoRef.current.srcObject = stream; videoRef.current.play(); } setIsCameraOpen(true); } catch (err) { console.error('摄像头调用失败:', err); alert('无法调用摄像头,请检查权限设置或设备连接'); } }; // 拍照功能逻辑 const takePhoto = () => { if (!videoRef.current || !canvasRef.current) return; const video = videoRef.current; const canvas = canvasRef.current; // 同步canvas尺寸和视频实际尺寸,避免截图变形 canvas.width = video.videoWidth; canvas.height = video.videoHeight; // 将当前视频帧绘制到canvas const ctx = canvas.getContext('2d'); ctx.drawImage(video, 0, 0, canvas.width, canvas.height); // 导出为base64格式图片,可直接渲染或上传到Node.js后端 const imgData = canvas.toDataURL('image/png'); setCapturedImg(imgData); }; // 关闭摄像头,释放资源 const closeCamera = () => { if (mediaStream) { mediaStream.getTracks().forEach(track => track.stop()); setMediaStream(null); setIsCameraOpen(false); } }; // 组件卸载时自动关闭摄像头,避免后台占用 useEffect(() => { return () => { if (mediaStream) { mediaStream.getTracks().forEach(track => track.stop()); } } }, [mediaStream]); return ( <div className={styles.cameraWrapper}> {/* 你原有待绑定的按钮代码 */} {!isCameraOpen && ( <div className={styles.cameraCont} onClick={openCamera}> <p>Open Camera</p> </div> )} {/* 摄像头预览区域 */} {isCameraOpen && ( <div className={styles.previewArea}> <video ref={videoRef} playsInline muted></video> {/* 隐藏的canvas元素,无需添加可见样式 */} <canvas ref={canvasRef} style={{ display: 'none' }}></canvas> <div className={styles.actionBtns}> <button onClick={takePhoto}>拍照</button> <button onClick={closeCamera}>关闭摄像头</button> </div> </div> )} {/* 拍摄结果预览 */} {capturedImg && ( <div className={styles.photoPreview}> <img src={capturedImg} alt="拍摄的照片" /> <button onClick={() => setCapturedImg(null)}>重新拍摄</button> </div> )} </div> ) }
注意事项
浏览器存在安全限制:
getUserMedia接口仅在localhost本地开发环境、或者配置了HTTPS证书的线上域名下可正常调用,普通HTTP环境调用会直接被浏览器拦截。移动端适配需要给
<video>标签加上playsInline属性,避免iOS Safari自动将视频流全屏播放。拿到的
capturedImg是base64格式的图片数据,如果需要上传到Node.js后端,可以直接将该字符串作为请求参数传递,后端解码后即可存储为静态文件。
内容的提问来源于stack exchange,提问作者Hadi Zouhbi
相关产品推荐
相关产品推荐

