美文网首页
H5调用摄像头扫脸拍照

H5调用摄像头扫脸拍照

作者: 疾风劲草ccy | 来源:发表于2022-10-28 18:08 被阅读0次
    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
    
    
    preview

    感谢浏览,欢迎评论指正,转载请标明出处。

    相关文章

      网友评论

          本文标题:H5调用摄像头扫脸拍照

          本文链接:https://www.haomeiwen.com/subject/bnrftdtx.html