import { useEffect, useRef, useState } from 'react'
import Button from '@/components/Button'
import icons from '@/assets/icons'
import { Toast } from 'antd-mobile'
import './index.less'
type Props = {
onSuccess: (photo: string) => void
onCancel: () => void
}
// 摄像头显示宽高
const CAMERA_WIDTH = 340
const CAMERA_HEIGHT = 340
const MediaTakePhoto = ({ onSuccess }: Props) => {
const [photo, setPhoto] = useState('')
const [position, setPosition] = useState<'front' | 'back'>('front') // 摄像头 前置/后置
const isPhoto = useRef<boolean>(false) // 已拍照
const video = useRef<any>(null)
const canvas = useRef<any>(null)
const MediaStreamTrack = useRef<any>(null) // 视频流
useEffect(() => {
getMedia()
return () => clearMedia()
}, [])
// 播放摄像头
const getMedia = (pos: 'front' | 'back' = 'front') => {
setPosition(pos)
// 摄像头配置
let constraints = {
video: {
width: CAMERA_WIDTH,
height: CAMERA_HEIGHT,
facingMode: pos === 'front' ? 'user' : 'environment',
},
audio: false,
}
// 获取视频流
navigator.mediaDevices
.getUserMedia(constraints)
.then((MediaStream: MediaStream | any) => {
console.log(MediaStream.getTracks())
MediaStreamTrack.current =
typeof MediaStream.stop === 'function' ? MediaStream : MediaStream.getTracks()[0] // 可能多个摄像头
if ('srcObject' in video.current) {
video.current.srcObject = MediaStream
} else {
// 有些旧浏览器没有srcObject
video.current.src = window.URL.createObjectURL(MediaStream)
}
video.current.onloadedmetadata = () => {
video.current.play()
}
isPhoto.current = false
})
.catch((error: Error) => {
Toast.show('请您允许使用相机')
console.error(error)
})
}
// 关闭摄像头
const clearMedia = () => {
if (MediaStreamTrack.current) {
MediaStreamTrack.current.stop()
MediaStreamTrack.current = null
}
}
// 切换摄像头
const switchCamera = () => {
getMedia(position === 'back' ? 'front' : 'back')
}
// 拍照
const takePhoto = () => {
if (isPhoto.current) {
getMedia(position)
clearCanvas()
isPhoto.current = false
} else {
let ctx = canvas.current.getContext('2d')
// 前置摄像头左右镜像翻转
if (position === 'front') {
ctx.translate(CAMERA_WIDTH, 0)
ctx.scale(-1, 1)
}
ctx.drawImage(video.current, 0, 0, CAMERA_WIDTH, CAMERA_HEIGHT)
setPhoto(canvas.current.toDataURL('image/jpeg'))
isPhoto.current = true
clearMedia()
}
}
// 重置canvas(清除照片)
const clearCanvas = () => {
if (canvas.current) {
/* eslint-disable-next-line */
canvas.current.height = canvas.current.height
}
}
// 重拍
const handleRephotograph = () => {
setPhoto('')
takePhoto()
}
// 确认
const handleConfirm = async () => {
onSuccess(photo)
}
return (
<div className="take-photo">
<div className="camera-wrap">
{!photo && (
<div className="btn-camera" onClick={switchCamera}>
<img className="icon-camera" src={icons.IconCamere} alt="" />
</div>
)}
<video
ref={video}
className="camera"
width={CAMERA_WIDTH}
height={CAMERA_HEIGHT}
autoPlay
playsInline // ios 不会自动播放
style={{ transform: `rotateY(${position === 'front' ? 180 : 0}deg)` }} // 前置摄像头左右镜像翻转
></video>
{/* 生成的图片 */}
<img className="photo" src={photo} alt="" style={{ zIndex: photo ? 2 : 0 }} />
{/* 画布,绝对定位放屏幕外,若直接把<canvas>放<video>上方,ios有时候会出现<canvas>遮挡<video> */}
<canvas
ref={canvas}
className="canvas"
width={CAMERA_WIDTH}
height={CAMERA_HEIGHT}
></canvas>
</div>
{photo ? (
<div className="btns">
<Button onClick={handleRephotograph}>重拍</Button>
<Button type="primary" onClick={handleConfirm}>确认</Button>
</div>
) : (
<Button onClick={takePhoto}>拍照</Button>
)}
</div>
)
}
export default MediaTakePhoto
![](https://img.haomeiwen.com/i697141/a531d160a3885baa.png)
感谢浏览,欢迎评论指正,转载请标明出处。
网友评论