美文网首页
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调用摄像头扫脸拍照

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

  • H5前端调用Android拍照功能

    JS调用Android摄像头拍照 产品经理要求H5要实现直接调用原生摄像头完成拍照功能: 一开始也是糊涂,我们前端...

  • uni-app摄像头扫码问题

    1. 官网扫码api说明 app端可直接调用api扫码(后置),前置扫码参考摄像头扫码功能封装 h5端不支持调用a...

  • 【 Android 】Camera2 初探

    调用手机摄像头,在当今的众多 App 中都有使用,比如:拍照、扫二维码等。 本篇文章将实现:调用系统相机进行拍照,...

  • 【总结】2017.01.10

    2017.01.10 - 计划 调用摄像头拍照取景 完善打卡功能 外包项目开发文档 - 实际完成 调用摄像头拍照取...

  • H5前端调用摄像头拍照

    需求:最近有个移动端开发需求,用户打开相机点击拍照按钮,就会拍一张照片(照片不进手机系统相册),显示在页面下部小视...

  • 使用js调用摄像头拍照

    使用js调用摄像头拍照 在一些浏览器里已经可以使用web api调用摄像头功能了。基于此可以经行拍照摄像功能,网上...

  • CameraX前置摄像头拍照镜像问题记录

    CameraX 拍照时遇到一个问题、记录一下 打开相机调用前置摄像头拍照 ,设置前置摄像头左右镜像。避免拍照出来预...

  • H5混合开发二维码扫描以及调用本地摄像头

    今天主管给了我个需求,说要用混合开发,用H5调用本地摄像头进行扫描二维码,我之前有做过原生安卓的二维码扫一扫,主要...

  • H5 使用移动端摄像头

    某些场景下,需要 H5 脱离应用来使用手机系统的摄像头用于照相或者拍摄。 目前而言,移动端使用 H5 调用摄像头有...

网友评论

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

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