一键调用手机相机摄像头

683 阅读4分钟

使用video标签以及canvas模拟相机

优点:

跨平台兼容性好

  • 通过调用 navigator.mediaDevices.getUserMedia,它能够兼容 iOS、Android 和桌面设备上的大多数现代浏览器。

缺点

  • 用户体验局限性

    • 在移动设备上,虽然 facingMode 可以选择摄像头方向,但用户不能像使用原生相机那样通过点击屏幕对焦或调整曝光等。它只是一个简单的摄像头预览。
    • 相比原生相机应用,此方法缺少一些高级功能,例如滤镜、调整焦距、亮度控制等。
  • 性能问题

    • 使用 canvas 捕获视频帧可能在某些性能较差的设备上出现卡顿,特别是在高分辨率视频的情况下。
    • 视频捕捉和图像渲染消耗 CPU 资源,若要处理大量图像或长时间使用,可能会导致性能问题。
  • 浏览器兼容性问题

    • 该功能依赖于 navigator.mediaDevices.getUserMedia,在某些旧的浏览器或特殊的安全环境下可能不支持,尤其是在不安全的 HTTP 环境中。
    • 在 iOS 的某些浏览器上,即使支持 getUserMedia,也可能会有一些限制或不一致的行为,尤其是在切换摄像头的过程中。
  • 用户权限问题

    • 每次调用摄像头时,浏览器会向用户请求权限。如果用户拒绝访问摄像头,组件将无法正常工作,可能需要处理权限拒绝的回退方案。
  • 照片质量

    • 由于使用的是 canvas 截取视频流,照片质量取决于视频的分辨率,可能无法达到原生相机应用捕捉的图像清晰度。
import React, { useEffect, useRef, useState } from 'react';
const CameraCapture: React.FC = () => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const [imageData, setImageData] = useState<string | null>(null);
  const [facingMode, setFacingMode] = useState<'user' | 'environment'>('user'); // 前置或后置摄像头

  useEffect(() => {
    // 调用设备摄像头
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: { facingMode } })
        .then((stream) => {
          if (videoRef.current) {
            videoRef.current.srcObject = stream;
          }
        })
        .catch((err) => {
          console.error('Error accessing the camera: ', err);
          alert('无法访问相机,请检查浏览器设置或使用 HTTPS 访问');
        });
    }
  }, [facingMode]);  // 当 facingMode 变化时重新调用摄像头

  const handleCapture = () => {
    const video = videoRef.current;
    const canvas = canvasRef.current;
    if (video && canvas) {
      // 设置 canvas 尺寸为视频的宽高
      canvas.width = video.videoWidth;
      canvas.height = video.videoHeight;

      // 获取 2D 上下文并将视频帧绘制到 canvas 上
      const ctx = canvas.getContext('2d');
      if (ctx) {
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

        // 将 canvas 转换为图片并保存到状态中
        const image = canvas.toDataURL('image/png');
        setImageData(image);  // 设置图片 base64 数据
        console.log(image);   // 可以在此输出 base64 数据
      }
    }
  };

  const handleSwitchCamera = () => {
    // 切换摄像头方向
    setFacingMode((prevMode) => (prevMode === 'user' ? 'environment' : 'user'));
  };

  return (
    <div>
      <video ref={videoRef} autoPlay style={{ width: '100%', maxWidth: '400px' }} />
      <button onClick={handleCapture}>Capture Photo</button>
      <button onClick={handleSwitchCamera}>Switch Camera</button>
      
      {/* 隐藏的 canvas 元素,用来捕获视频帧 */}
      <canvas ref={canvasRef} style={{ display: 'none' }}></canvas>

      {/* 如果有图片数据,显示图片 */}
      {imageData && (
        <div>
          <h3>Captured Image:</h3>
          <img src={imageData} alt="Captured" style={{ maxWidth: '100%' }} />
        </div>
      )}
    </div>
  );
};

export default CameraCapture;

使用input标签直接调用相机功能

import React, { useState } from 'react';

const CameraCapture: React.FC = () => {
  const [imageData, setImageData] = useState<string | null>(null);

  const handleCapture = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (file) {
      const reader = new FileReader();
      reader.onloadend = () => {
        setImageData(reader.result as string); // 设置图片的 base64 数据
      };
      reader.readAsDataURL(file);
    }
  };

  return (
    <div>
      <input
        type="file"
        accept="image/*"
        capture // 直接调用相机
        onChange={handleCapture}
      />
      
      {/* 如果有图片数据,显示图片 */}
      {imageData && (
        <div>
          <h3>Captured Image:</h3>
          <img src={imageData} alt="Captured" style={{ maxWidth: '100%' }} />
        </div>
      )}
    </div>
  );
};

export default CameraCapture;

优点:

  1. 简单实现

    • 使用 <input> 标签原生支持的 type="file"capture 属性,简洁明了,避免了复杂的 getUserMedia API 调用。
    • 很容易捕捉到用户通过相机拍摄的图片,无需额外操作。
  2. 跨平台兼容性较好

    • 对于大多数移动设备(iOS 和 Android),通过设置 capture 属性可以直接调用相机拍照。
    • 这种方式是标准的 HTML 元素,可以在大多数现代浏览器和移动设备上正常工作。
  3. 文件选择回退机制

    • 如果设备不支持调用相机(例如台式机),用户可以选择从文件系统中上传图片。这为不支持 capture 的设备提供了回退机制。
  4. 支持文件读取

    • 使用 FileReader 轻松地将图像文件转换为 base64 格式的字符串,这对于预览或上传图片非常方便。

缺点:

  1. 用户体验受限

    • 通过 <input> 标签调用相机时,用户体验较为简单,无法实现诸如自动聚焦、滤镜等更高级的功能。
    • 在某些 iOS 设备上,尽管设置了 capture 属性,用户依然可能会看到一个文件选择界面,而不是直接进入相机界面。
  2. 浏览器和设备支持不一致

    • iOS 设备(尤其是某些 Safari 浏览器版本)在调用相机时行为不一致,有时会打开文件选择器而非相机。不同设备的支持程度可能有差异。
    • 桌面浏览器即便支持 capture 属性,也无法调用摄像头,只能选择文件。
  3. 相机控制受限

    • 无法像使用 getUserMedia 那样指定前置或后置摄像头,因此在移动设备上默认的摄像头(通常是前置)可能无法满足某些场景需求。
    • 对摄像头的访问权限完全交给了系统,开发者无法获取到更多控制细节(例如切换摄像头)。