H5 页面通过navigator.mediaDevices.getUserMedia调用手机摄像头拍照上传

这篇具有很好参考价值的文章主要介绍了H5 页面通过navigator.mediaDevices.getUserMedia调用手机摄像头拍照上传。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求: 某知名化妆品牌,要做个在线问卷调查。需要试用着自拍上传

注:使用navigator.mediaDevices.getUserMedia 需要使用https请求协议否者视为不安全,无法访问,开发阶段需要将启动改为https 在package.json中

前端开发环境启动项目将http协议改为https协议

H5 页面通过navigator.mediaDevices.getUserMedia调用手机摄像头拍照上传H5 页面通过navigator.mediaDevices.getUserMedia调用手机摄像头拍照上传H5 页面通过navigator.mediaDevices.getUserMedia调用手机摄像头拍照上传

 

 授权,默认前置,切换后置

注意切换摄像头时要注意,一定要像关闭摄像头不然会有问题在部分手机上文章来源地址https://www.toymoban.com/news/detail-504529.html

import {
  Button,
  Dialog,
  ImageUploader,
  Modal,
  Popup,
  Toast,
} from 'antd-mobile';
import React, { useEffect, useState } from 'react';
import { useRef } from 'react';
import { history } from 'umi';
import {
  uploadFile,
  getProjectInfo,
  saveUploadFile,
  deleteFile,
  getQuestionsByResultId,
} from '@/app/request/requestApi'; // 接口
import { ImageUploadItem } from 'antd-mobile/es/components/image-uploader';
import { CameraOutline, ExclamationCircleFill } from 'antd-mobile-icons';
import './style.less';

const index = (props: any) => {
  const [fileList, setFileList] = useState<any[]>([]);
  const [projectInfo, setProjectInfo] = useState<any>({});
  const [visible, setVisible] = useState(false);
  const [rotvisible, setRotVisible] = useState(false);
  const [direction, setDirection] = useState(0);
  const [language, setLanguage] = useState('ZH');

  useEffect(() => {
    setLanguage(localStorage.getItem('language') ?? 'ZH');
  }, []);

  useEffect(() => {
    setTimeout(() => {
      document.getElementsByTagName('title')[0].text = '上传图片';
    }, 500);
    getProjectInfo().then((res) => {
      if (res.success) {
        localStorage.setItem('projectInfo', JSON.stringify(res.data));
        setProjectInfo(res.data);
      } else {
        Dialog.alert({
          content: res.msg,
          onConfirm: () => {
            history.push('../');
          },
        });
      }
    });
  }, []);
  useEffect(() => {
    if (!visible) {
       // 关闭
      if (document.getElementById('video')?.srcObject) {
        document.getElementById('video').srcObject.getTracks()[0].stop();
      }
      return;
    }
    if (
      //@ts-ignore
       // 开启
      navigator.mediaDevices.getUserMedia ||
      navigator.getUserMedia ||
      navigator.webkitGetUserMedia ||
      navigator.mozGetUserMedia ||
      navigator.msGetUserMedia ||
      navigator.oGetUserMedia
    ) {
      var facingMode: any = null;
      if (direction == 1) {
        facingMode = { exact: 'environment' };
      } else {
        facingMode = { exact: 'user' };
      }
      // 切换摄像头需先关闭再打开
      if (document.getElementById('video')?.srcObject) {
        document.getElementById('video').srcObject.getTracks()[0].stop();
      }
      //调用用户媒体设备, 访问摄像头
      getUserMedia(
        { video: { width: 480, height: 320, facingMode: facingMode } }, // user 前置
        success,
        error,
      );
    } else {
      alert('不支持访问用户媒体');
    }
  }, [visible, direction]);
  let model: any = localStorage.getItem('model');
  const onUpload = async (file: any) => {
    let formData = new FormData();
    formData.append('projectId', projectInfo.projectId);
    formData.append('projectTimeId', projectInfo.timeId);
    formData.append('phone', model);
    formData.append('fileInfos', file);
    var res = await uploadFile(formData);
    if (res.success) {
      return {
        url: res.data[0].imageUrl,
      } as ImageUploadItem;
    } else {
      Dialog.alert({
        content: res.msg,
        onConfirm: () => {
          //history.push("../");
        },
      });
      return {
        url: URL.createObjectURL(file),
      } as ImageUploadItem;
    }
  };

  const getUserMedia = (constraints: any, success: any, error: any) => {
    if (navigator.mediaDevices.getUserMedia) {
      //最新的标准API
      navigator.mediaDevices
        .getUserMedia(constraints)
        .then(success)
        .catch(error);
    } else if (navigator.webkitGetUserMedia) {
      //webkit核心浏览器
      navigator.webkitGetUserMedia(constraints, success, error);
    } else if (navigator.mozGetUserMedia) {
      //firfox浏览器
      navigator.mozGetUserMedia(constraints, success, error);
    } else if (navigator.getUserMedia) {
      //旧版API
      navigator.getUserMedia(constraints, success, error);
    }
  };

  const success = (stream: any) => {
    //兼容webkit核心浏览器
    let CompatibleURL = window.URL || window.webkitURL;
    //将视频流设置为video元素的源
    let videoElement = document.getElementById('video');
    //@ts-ignore
    videoElement.srcObject = stream;
    //@ts-ignore
    videoElement.play();
  };

  const error = (error: any) => {
    alert(`访问用户媒体设备失败${error.name}, ${error.message}`);
    console.log(`访问用户媒体设备失败${error.name}, ${error.message}`);
  };

  return (
    <>
      <span className="projectTitle">{projectInfo.projectName}</span>
      <ImageUploader
        style={{ '--cell-size': '98px' }}
        value={fileList}
        onDelete={(item: any) => {
          console.log(item);
          return Dialog.confirm({
            content: '是否确认删除',
          }).then((res) => {
            if (!res) {
              return false;
            } else {
              return deleteFile({
                id: item.id,
              }).then((res) => {
                if (res.success) {
                  return true;
                } else {
                  Dialog.alert({
                    content: res.msg,
                    onConfirm: () => {
                      //history.push('../');
                    },
                  });
                  return false;
                }
              });
            }
          });
        }}
        onChange={(files) => {
          setFileList(files);
        }}
        disableUpload={true}
        upload={onUpload}
        children={
          <div
            style={{
              width: 98,
              height: 98,
              //borderRadius: 40,
              backgroundColor: '#f5f5f5',
              display: 'flex',
              justifyContent: 'center',
              alignItems: 'center',
              color: '#999999',
            }}
          >
            <CameraOutline
              onClick={() => {
                Dialog.alert({
                  content:
                    language == 'ZH'
                      ? '请摘掉眼镜,露出额头和耳朵,在光线明亮的地方,原地旋转每30度左右拍一张,共6张'
                      : 'Please take off your glasses, expose your forehead and ears, and take a picture every 60 degrees or so in a bright place. A total of 6 pictures',

                  onConfirm: () => {
                    setVisible(true);
                  },
                });
              }}
              style={{ fontSize: 32 }}
            />
          </div>
        }
      />
      <div style={{ marginLeft: '20px' }}>
        <ExclamationCircleFill style={{ color: 'rgb(255,51,102)' }} />
        <span>
          {language == 'ZH'
            ? '上传图片需最少六张图片才可进入下一步'
            : 'A minimum of six images are required to upload images to proceed to the next step'}
        </span>
      </div>
      <div
        style={{
          position: 'absolute',
          bottom: '8px',
          textAlign: 'center',
          width: '100%',
        }}
      >
        <Button
          className="btnNext"
          onClick={() => {
            if (fileList.length < 6) {
              Toast.show({
                icon: 'fail',
                content:
                  language == 'ZH'
                    ? '请至少上传6张照片!'
                    : 'Please upload at least 6 photos!',
              });
              return;
            }

            saveUploadFile({
              projectId: projectInfo.projectId,
              projectTimeId: projectInfo.timeId,
              phone: model,
              PhoneUniqueId: localStorage.getItem('PhoneUniqueId'),
            }).then((res) => {
              if (res.success) {
                if (!projectInfo.timeId) {
                  Modal.show({
                    closeOnMaskClick: false,
                    bodyStyle: {
                      width: '270px',
                      height: '220px',
                      textAlign: 'center',
                      paddingTop: '47px',
                    },
                    showCloseButton: false,
                    header: null,
                    actions: [],
                    content: (
                      <>
                        <img className="imgSuccess" />
                        <div
                          style={{
                            marginTop: '14px',
                            fontSize: '14px',
                            fontWeight: 'bold',
                            color: '#333333',
                          }}
                        >
                          {' '}
                          {language == 'ZH'
                            ? '感谢您的参与!'
                            : 'Thank you for your participation.'}
                        </div>
                      </>
                    ),
                  });
                  return;
                }
                localStorage.setItem('resultId', res.data);
                localStorage.setItem('files', JSON.stringify(fileList));
                getQuestionsByResultId({
                  resultId: res.data,
                }).then((res) => {
                  if (res.success) {
                    localStorage.setItem(
                      'question',
                      JSON.stringify(res.data.projectTimeInfo[0]),
                    );
                    history.push('form');
                  } else {
                    Dialog.alert({
                      content: res.msg,
                      onConfirm: () => {},
                    });
                  }
                });
              } else {
                Dialog.alert({
                  content: res.msg,
                  onConfirm: () => {},
                });
              }
            });
          }}
        >
          {language == 'ZH' ? ' 下一步 >' : 'Next >'}
        </Button>
      </div>
      <Popup
        visible={visible}
        onMaskClick={() => {
          setVisible(false);
        }}
        forceRender={true}
        position="top"
        bodyStyle={{
          minWidth: '100%',
          minHeight: '100%',
          backgroundColor: 'black',
        }}
      >
        <div
          style={{
            width: '100%',
            height: '60px',
            textAlign: 'center',
            lineHeight: '75px',
            paddingLeft: '80%',
          }}
        >
          // 切换摄像头
          <CameraOutline
            onClick={() => {
              setDirection(direction == 1 ? 0 : 1);
              console.log('切换摄像头');
            }}
            style={{ fontSize: '30px', lineHeight: '60px', color: 'white' }}
          />
        </div>
        //  video 视频
        <video
          id="video"
          width="100%"
          webkit-playsinline="true"
          playsInline={true}
        ></video>
        <Button
          onClick={() => {
            setVisible(false);
          }}
          style={{
            position: 'absolute',
            left: '20px',
            bottom: '30px',
            backgroundColor: 'transparent',
            border: 'none',
            zIndex: '999',
            color: 'white',
          }}
        >
          取消
        </Button>
         // 拍照截取当前帧绘制到画布上
        <div className="divBtn">
            // 主要是获取摄像头的视频流并显示在Video 签中
          <CameraOutline
            onClick={() => {
              var canvas = document.getElementById('canvas');
              canvas.width = document.getElementById('video')?.offsetWidth;
              canvas.height = document.getElementById('video')?.offsetHeight;
              //@ts-ignore
              canvas
                ?.getContext('2d')
                ?.drawImage(
                  document.getElementById('video'),
                  0,
                  0,
                  document.getElementById('video')?.offsetWidth,
                  document.getElementById('video')?.offsetHeight,
                );
              canvas.getContext('2d')?.canvas.toBlob(
                (blob: any) => {
                  let files = new window.File(
                    [blob],
                    new Date().getTime() + '.jpg',
                  );
                  let formData = new FormData();
                  formData.append('projectId', projectInfo.projectId);
                  formData.append('projectTimeId', projectInfo.timeId);
                  formData.append(
                    'phone',
                    model,
                    // navigator.userAgent.split('AppleWebKit')[0],
                  );
                  formData.append('fileInfos', files);
                  setVisible(false);
                  uploadFile(formData).then((res) => {
                    if (res.success) {
                      setFileList([
                        ...fileList,
                        {
                          key: new Date().getTime(),
                          url: URL.createObjectURL(files),
                          id: res.data[0].id,
                        },
                      ]);
                    } else {
                      Dialog.alert({
                        content: res.msg,
                        onConfirm: () => {},
                      });
                    }
                  });
                },
                'image/jpeg',
                0.7,
              );

              //@ts-ignore
              var cutAvater = canvas.getContext('2d')?.canvas.toDataURL(0.7);
              var arr = cutAvater?.split(',');
              //@ts-ignore
              var data = window.atob(arr[1]);
              //@ts-ignore
              var mime = arr[0].match(/:(.*?);/)[1];
              var ia = new Uint8Array(data.length);
              for (var i = 0; i < data.length; i++) {
                ia[i] = data.charCodeAt(i);
              }
              var blob = new Blob([ia], { type: 'image/jpeg' });
            }}
            className="camerBtn"
          />
        </div>
      </Popup>
      <canvas
        style={{ position: 'absolute', left: '-1500px' }}
        id="canvas"
        // width="480"
        // height="320"
      ></canvas>
    </>
  );
};

export default index;
main {
  padding: 24px 24px 16px;
  display: flex;
}

video {
  margin-right: 16px;
  box-sizing: content-box;
  //border: 4px solid #ffaabb;
}

canvas {
  box-sizing: content-box;
  border: 4px solid #aabbff;
}

.actions {
  padding: 0 24px;
}

.actions button {
  margin-right: 16px;
}

.imgSuccess {
  width: 142px;
  height: 119px;
  background: url('../../../assets/images/success.png') no-repeat;
  background-size: 100%;
  border: 1px dashed gray;
}

.projectTitle {
  display: block;
  font-size: 26px;
  color: #b18b35;
  font-weight: bold;
  font-family: 'MicrosoftYaHei-Bold';
  margin: 20px 0 21px 20px;
}

.adm-image-uploader .adm-space-item {
  width: 100px;
  height: 100px;
  border: 1px dashed black;
  padding-bottom: 0px !important;
  margin-top: 10px;
}

.adm-image-uploader {
  margin-bottom: 40px;
}
.adm-image-uploader {
  margin-left: 20px;
}

.adm-text-area-element {
  background-color: #ffffff !important;
}

.btnNext {
  color: white;
  font-size: 12px;
  font-weight: bold;
  background-color: #b18b34;
  border: none;
  width: 236px;
  height: 39px;
  border-radius: 18px;
}

.adm-image-img {
  // width: auto;
  // height: 100%;
  // margin:0 auto;
}

.camerBtn {
  font-size: 60px;
  color: white;
}

.divBtn {
  position: absolute;
  bottom: 0px;
  width: 100%;
  height: 80px;
  text-align: center;
}

到了这里,关于H5 页面通过navigator.mediaDevices.getUserMedia调用手机摄像头拍照上传的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • H5页面唤起手机拨打电话(拨号)

    在移动端页面开发中,偶尔会需要唤起用户手机的打电话功能,拨打客服电话,此时我们可以按照以下操作实现打电话功能: 拨打电话 index.html在中加入这一段 js 或者 发送短信 index.html在 head/head 中加入这一段 js 或者 移动web页面自动探测电话号码 使用wtai协议进行拨打电话

    2024年02月12日
    浏览(9)
  • 手机端H5页面判断是否横屏

    之前,做了一个H5项目,需要在横竖屏变化时,做一些处理,一般先要判断手机是否横屏,在不同状态下添加不同效果 方法一:JS判断 移动端的浏览器一般都支持window.orientation这个参数,通过这个参数可以判断出手机是处在横屏还是竖屏状态 屏幕方向对应的window.orientation值:

    2024年02月11日
    浏览(11)
  • 微信小程序嵌套H5页面,调用微信小程序扫码功能,并将结果传回H5页面

    微信小程序嵌套H5页面,调用微信小程序扫码功能,并将结果传回H5页面

    实现方式: 小程序嵌套h5页面,点击h5页面的扫码按钮跳转到小程序的扫码页面,进入之后会立即扫码,拿到扫码结果后,跳转到小程序定义好的webview页面,再由webview页面进入h5页面。 缺点:为了唤起扫码,会进入一个空白的扫码页面 1、小程序嵌套h5页面方法 在小程序中,创

    2024年02月12日
    浏览(9)
  • uniapp H5页面、小程序页面获取手机号拨打电话

    uniapp H5页面、小程序页面获取手机号拨打电话

    效果图: 1、H5页面----手机号写死: 2、H5页面----动态获取手机号拨打: APP: 小程序: methods:

    2024年02月07日
    浏览(16)
  • 记录--h5调用手机摄像头踩坑

    记录--h5调用手机摄像头踩坑

    一般业务也很少接触摄像头,有也是现成的工具库扫个二维码。难得用一次,记录下踩坑。 这个就不用多说了,缺点就是没办法自定义界面,它是调用的系统原生相机界面。 由于我需要自定义界面,就像下面这样: 所以我选择了这个方案,这个 api 使用起来其实很简单: 可

    2024年02月08日
    浏览(16)
  • 微信小程序web-view嵌入uni-app H5页面,通过H5页面传参给小程序进行转发分享页面,并通过点击转发出来的卡片,定向打开对应H5路径

    index.wxml  index.js 在H5项目的App.vue页面获取参数实现自动跳转到对应页面,包括携带的参数值

    2024年02月12日
    浏览(47)
  • ios手机在app中调试h5页面

    ios手机在app中调试h5页面

    网页开发在浏览器里调试很方便,但是在移动端开发调试,例如需要在app中打开,会用到一些bridge , 这时候就不能在浏览器调试。在app调试,如果每次都要发布到测试环境才能调试,那就会浪费很多时间。 可以通过charls来做一个代理 从而在手机app里调试h5页面 安装charles 安装

    2024年03月15日
    浏览(16)
  • h5调用手机摄像头获取图片用于人脸识别

    1、安卓手机获取前置摄像头,并在video标签显示 注:navigator.mediaDevices.getUserMedia文档说明兼容Safari11,实测不好用 2、苹果手机获取前置摄像头拍照上传,用于人脸识别 注:该方法在安卓手机也可使用 3、附件 //exif.js exif.js //rotate-photo.js rotate-photo.js

    2024年02月11日
    浏览(11)
  • 微信小程序中的H5页面如何调用地理位置导航

    在微信小程序的H5页面中,我们经常需要使用地理位置导航功能,以便为用户提供准确的导航服务。下面将介绍如何在微信小程序的H5页面中调用地理位置导航功能,并提供相应的源代码示例。 获取用户地理位置 在调用地理位置导航之前,首先需要获取用户的地理位置信息。

    2024年02月04日
    浏览(15)
  • 微信小程序通过扫一扫调用H5项目

    业务逻辑: 小程序为主体,外链一个H5项目,相当于在小程序webView几个页面及功能。 现需在小程序扫一扫点击事件触发后通过二维码生成的url跳转到H5项目相关页面 PS:二维码生成可查看这里 一、小程序 二、H5项目 PS:需求中遇到了这种情况,所以这里记录一下,方便自己的

    2024年02月04日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包