React+TypeScript手写拍照上传

前言

最近写了个招新面试系统,要求能支持拍照上传简历图片。经过对其义务,api的了解。用** React+TypeScript **手写出了一个原生的拍照上传组件,写此博客对此加以记录,也会公开到github方便日后的使用。

核心代码函数

1.调用摄像头

主要使用的是浏览器自带的api

typescript 复制代码
navigator.mediaDevices.getUserMedia

该api可以调用浏览器的摄像头权限,返回的是promise函数

typescript 复制代码
  mediaStream.getTracks().forEach((track) => {
    track.stop();
  });

具体代码:

typescript 复制代码
useEffect(() => {
  //组件挂载调用函数
  navigator.mediaDevices
    .getUserMedia({
      video: {
        width: 1080,
        height: 1920,
        frameRate: 30,
        facingMode: state.facing//调整摄像头的正反面
      },
      audio: false
    })
    .then((stream) => {
      mediaStream = stream;
      if (videoRef.current) {
        videoRef.current.srcObject = stream;
        videoRef.current.play();
      }
    })
    .catch((err) => {
      console.log(`An error occurred: ${err}`);
    });
  return () => {
    // 组件卸载时的逻辑
    if (mediaStream) {
      mediaStream.getTracks().forEach((track) => {
        track.stop();
      });
    }
  };
}, [state.facing]);

2.处理视频可播放事件

根据组件大小,计算视频流的宽高

typescript 复制代码
const handleCanPlay = () => {
  if (!state.streaming) {
    const height =
      (videoRef.current?.videoHeight as number) /
      (((videoRef.current?.videoWidth as number) / state.width) as number);
    setState({
      ...state,
      streaming: true,
      height: height as number
    });
  }
};

3.处理拍照事件

使用canvas来绘画视频流得出的照片结果

并调用onUploadPhoto回调函数将照片数据URL传递给父组件。

typescript 复制代码
const handleTakePhoto = () => {
  if (videoRef.current && canvasRef.current) {
    const { width, height } = state;
    canvasRef.current.width = width;
    canvasRef.current.height = height;
    const context = canvasRef.current.getContext('2d');
    if (context) {
      context.drawImage(videoRef.current, 0, 0, width, height);
      const photo = canvasRef.current.toDataURL('image/png');
      setState({ ...state, photo });
      onUploadPhoto(photo);
    }
  }
};

代码的封装

1.依赖引入

typescript 复制代码
import React, { useRef, useState, useEffect, ReactNode } from 'react';

2.定义组件接口类型:

typescript 复制代码
interface CameraProps {
  children?: ReactNode;
  onUploadPhoto: (photo: string) => void;//上传的函数
  onUnloadPhoto: (unload: boolean) => void;//卸载的函数
}

CameraProps接口定义了组件的属性类型。它包含了可选的children属性,以及onUploadPhoto和onUnloadPhoto两个回调函数属性。

3.定义组件状态

typescript 复制代码
interface CameraState {
  streaming: boolean;
  width: number;
  height: number;
  photo: string | undefined;
  facing: 'user' | 'environment';
}

CameraState接口定义了组件的状态类型。它包含了streaming表示是否正在录制、width和height表示视频的宽度和高度、photo表示拍摄的照片数据URL,以及facing表示相机的朝向。

4.定义相机组件:

typescript 复制代码
const Camera: React.FC<CameraProps> = ({ onUploadPhoto, onUnloadPhoto }) => {
  // ...
}

Camera是一个函数组件,接受CameraProps作为属性。组件内部使用了useState和useRef来创建状态和引用。

5.定义组件的状态和引用:

typescript 复制代码
const videoRef = useRef<HTMLVideoElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
let mediaStream: MediaStream | null = null;
const [state, setState] = useState<CameraState>({
  streaming: false,
  width: 320,
  height: 0,
  photo: undefined,
  facing: 'environment'
});

这段代码使用useRef创建了videoRef和canvasRef两个引用,分别指向视频元素和画布元素。mediaStream是一个变量用于存储媒体流对象。useState创建了state状态对象和对应的setState函数。

6.处理切换相机事件:

typescript 复制代码
const handleToggleFacing = () => {
  setState({ ...state, facing: state.facing === 'user' ? 'environment' : 'user' });
};

7.渲染组件:

typescript 复制代码
return (
  <div>
    <video ref={videoRef} onCanPlay={handleCanPlay} />
    <canvas ref={canvasRef} style={{ display: 'none' }} />
    {state.streaming && (
      <div>
        <button onClick={handleTakePhoto}>Take Photo</button>
        <button onClick={handleToggleFacing}>Toggle Camera</button>
      </div>
    )}
    {state.photo && <img src={state.photo} alt="Captured" />}
    {children}
  </div>
);

完整代码:

github

https://github.com/wzz778/aZeCamera

使用效果

手动马赛克🤣

拍照界面

上传界面

点击上传,调用handleUploadPhoto函数,返回图片的64编码

后言

功能pc,移动端都能使用,但样式主要适配了移动端,样式也可根据自己需求自行调整。

相关推荐
随风一样自由3 分钟前
【AI全栈+前端代理】前端代理配置中最常用的参数及说明
前端·前端代理
Lorin 洛林1 小时前
一文读懂 Agent Skills
前端·网络
newbe365242 小时前
我们如何使用 impeccable 优化前端界面设计与实现稳定性
前端·人工智能·分布式·github·aigc·wpf
KaMeidebaby9 小时前
卡梅德生物技术快报|蛋白 N 端测序在重组贻贝融合蛋白表征中的应用,解决原核表达序列偏移工艺难题
前端·人工智能·物联网·算法·百度
kyriewen10 小时前
我筛了 1400 个 Claude Code Skills,留下 5 个天天在用的
前端·ai编程·claude
JNX_SEMI10 小时前
AT2401C 2.4GHz 全集成射频前端单芯片技术解析
前端·单片机·嵌入式硬件·物联网·硬件工程
anOnion11 小时前
Agentic 前端开发之 实时显示 AI Agent 终端输出
前端·javascript·人工智能
随风一样自由11 小时前
【前端领域】2026最新前端领域全梳理(框架/工具/AI/跨端/底层标准/就业趋势)
前端·人工智能·前端框架
这是个栗子11 小时前
【前端性能优化】优化数据加载:用 Promise.all 从串行到并行
前端·javascript·性能优化·异步编程·前端优化·promise.all
fei_sun12 小时前
黑洞路由(Null Route/空接口路由)
服务器·前端·javascript