You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 23:54:18