前言

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

核心代码函数

1.调用摄像头

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

navigator.mediaDevices.getUserMedia

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

mediaStream.getTracks().forEach((track) => {

track.stop();

});

具体代码:

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.处理视频可播放事件

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

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传递给父组件。

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.依赖引入

import React, { useRef, useState, useEffect, ReactNode } from 'react';

2.定义组件接口类型:

interface CameraProps {

children?: ReactNode;

onUploadPhoto: (photo: string) => void;//上传的函数

onUnloadPhoto: (unload: boolean) => void;//卸载的函数

}

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

3.定义组件状态

interface CameraState {

streaming: boolean;

width: number;

height: number;

photo: string | undefined;

facing: 'user' | 'environment';

}

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

4.定义相机组件:

const Camera: React.FC = ({ onUploadPhoto, onUnloadPhoto }) => {

// ...

}

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

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

const videoRef = useRef(null);

const canvasRef = useRef(null);

let mediaStream: MediaStream | null = null;

const [state, setState] = useState({

streaming: false,

width: 320,

height: 0,

photo: undefined,

facing: 'environment'

});

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

6.处理切换相机事件:

const handleToggleFacing = () => {

setState({ ...state, facing: state.facing === 'user' ? 'environment' : 'user' });

};

7.渲染组件:

return (

{state.streaming && (

)}

{state.photo && react.js 前端 React+TypeScript手写拍照上传  第1张}

{children}

);

完整代码:

github

https://github.com/wzz778/aZeCamera

使用效果

手动马赛克藍

拍照界面

上传界面

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

后言

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

精彩内容

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。