方式吊起拍照的为系统相机,给用户的体验并不是很好,没有裁切框,也无法在系统相机上附加 tips 蒙层进行扩展,比如在蒙层上告...">
最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • H5实现自定义身份证拍照

    正文概述 掘金(EricLee)   2021-04-26   2238

    背景

    由于h5通过 <input type="file /> 方式吊起拍照的为系统相机,给用户的体验并不是很好,没有裁切框,也无法在系统相机上附加 tips 蒙层进行扩展,比如在蒙层上告知用户拍照的注意事项。所以业务上需要实现一个自定义拍照身份证的页面。

    前期准备工作

    各端兼容性现状

    结论: 安卓:chrome53版本之后支持该api。 ios:仅safari11+支持。ios微信内置浏览器、Chrome、Edge等其它浏览器均不支持。

    考虑替代方案

    以下情况,均需要考虑替代方案: 第一类:当满足下列条件,均需要采用系统相机拍照方案 1.用户不提供摄像头权限。 2.命中以下其中任意一条错误

    • AbortError[中止错误]
    • NotAllowedError[拒绝错误]
    • NotFoundError[找不到错误]
    • NotReadableError[无法读取错误]
    • OverConstrainedError[无法满足要求错误]
    • SecurityError[安全错误]
    • TypeError[类型错误]

    3.用户浏览器不支持该api ** 第二类:当ios用户使用非safari浏览器访问h5页面时 由于ios只有safari11+可以吊起后置摄像头视频流,如果ios用户在非safari浏览器打开h5登陆页,都要直接引导用户复制链接到safari浏览器打开,避免接下来无法进行自定义拍照。这里牛客做的就比较好,可以仿照牛客做一个引导按钮。

    H5实现自定义身份证拍照

    正片开始

    我们的主角是 MediaDevices.getUserMedia() , MDN 对该api的介绍如下

    能力检测


    由于不同浏览器对于标准的实现不一致,需要作api能力的兼容,避免用户浏览器无法正常调用该api。

    //访问用户媒体设备的兼容方法
    function getUserMedia(constrains) {
        if (navigator.mediaDevices?.getUserMedia) {
            //最新标准API
            return navigator.mediaDevices.getUserMedia(constrains);
        } else if (navigator.webkitGetUserMedia) {
            //webkit内核浏览器
            return navigator.webkitGetUserMedia(constrains);
        } else if (navigator.mozGetUserMedia) {
            //Firefox浏览器
            return navigator.mozGetUserMedia(constrains);
        } else if (navigator.getUserMedia) {
            //旧版API
            return navigator.getUserMedia(constrains);
        }
    }
    

    在页面上放置一个video元素


    <video
       id="video"
       autoPlay
       muted
       playsInline
       style={{
        width: '100%',
      }}
    ></video>
    

    有几个注意点⚠️

    对于这两种类型的视频,可以通过 <video autoplay>video.play() 两种方式来自动播放,无需用户主动操作。但是,如果它们在播放时变得有声音(获取了音轨,或者 muted 属性被取消),Safari 会暂停播放。

    • 只有提供 muted 属性,让视频静音,才可以通过 <video autoplay>video.play() 两种方式来进行播放
    • 必须提供 playsInline 属性,不然在ios上会只播放一帧

    调用封装好的getUserMedia,获取用户媒体流


    调用时,我们可以给constrains 对象可以多种不同的值,来获取用户设备底层各种不同的媒体流。

    • video: true (默认调取前置摄像头)
    • 为了调取后置摄像头,需要通过 facingMode: { exact: 'environment' } 来进行调用**(如果后置摄像头不存在,则会导致获取媒体流失败
    • 为了获取特定分辨率的视频流,我们可以指定相应的 width height(但这种方式有缺陷,一旦用户设备不存在对于像素流,则会导致获取媒体流失败,所以,我们不对像素进行定制,使用自动获取到的媒体流像素)
    /**
     * 该函数需要接受一个video的dom节点作为参数
     */
    function getUserMediaStream(videoNode) {
      	/** 
         * 调用api成功的回调函数
         */
        function success(stream, video) {
            return new Promise((resolve, reject) => {
                video.srcObject = stream;
    
                video.onloadedmetadata = function () {
                    video.play();
                    resolve();
                };
            });
        }
      
        //调用用户媒体设备,访问摄像头
        return getUserMedia({
            audio: false,
            video: { facingMode: { exact: 'environment' } },
            // video: true,
            // video: { facingMode: { exact: 'environment', width: 1280, height: 720 } },
        })
            .then(res => {
                return success(res, videoNode);
            })
            .catch(error => {
                console.log('访问用户媒体设备失败:', error.name, error.message);
                return Promise.reject();
            });
    }
    
    

    当前效果:

    H5实现自定义身份证拍照

    增加裁切框和外部阴影


    • 裁切框我们根据需求写到页面中,之后会通过 getBoundingClientRect 获取裁切框的位置进行裁切。
    • 外部阴影使用 box-shadow 即可
    <div className={styles['shadow-layer']} style={{ height: `${videoHeight}px` }}>
      <div id="capture-rectangle" className={styles['capture-rectangle']}></div>
    </div>
    
    @function remB($px) {
        @return ($px/75) * 1rem;
    }
    
    .shadow-layer {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      z-index: 1;
      overflow: hidden;
    
      .capture-rectangle {
        margin: remB(200) auto 0;
        width: remB(700);  // 这里写上我们需要裁切的宽
        height: remB(450); // 这里写上我们需要裁切的高
        border: 1px solid #fff;
        border-radius: remB(20);
        z-index: 2;
        box-shadow: 0 0 0 remB(1000) rgba(0, 0, 0, 0.7); // 外层阴影
      }
    }
    

    当前效果:

    H5实现自定义身份证拍照

    完成实时照片裁切,上传服务端进行OCR识别


    裁切用到的是 canvas.getContext('2d).drawImage 的能力。 void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

    可以我们传入 video 作为 source 进行裁切。 这里要注意

    • sxsy 对应的是距离真实 video 元素的 top left 距离,不是页面中 video 的大小,拿到裁切框位置大小之后,需要做转换,再进行裁切,否则裁切位置会对不上。
    
    /**
    * 获取video中对应的真实size
    */
    function getXYRatio() {
      // videoHeight为video 真实高度
      // offsetHeight为video css高度
      const { videoHeight: vh, videoWidth: vw, offsetHeight: oh, offsetWidth: ow } = video;
    
      return {
        yRatio: height => {
          return (vh / oh) * height;
        },
        xRatio: width => {
          return (vw / ow) * width;
        },
      };
    }
    
    

    在调用 getUserMediaStream 成功之后,我们开始捕捉视频流,每隔几秒进行截图,发送到服务器。

    /** 裁切上传相关核心代码  */
    const Photo = () => {
    		const [videoHeight, setVideoHeight] = useState(0);
        const ref = useRef(null);
    
        useEffect(() => {
            const video = document.getElementById('video');
            const rectangle = document.getElementById('capture-rectangle');
            const _canvas = document.createElement('canvas');
            _canvas.style.display = 'block';
    
            getUserMediaStream(video)
                .then(() => {
                    setVideoHeight(video.offsetHeight);
                    startCapture();
                })
                .catch(err => {
                    showFail({
                      text: '无法调起后置摄像头,请点击相册,手动上传身份证',
                      duration: 6,
                    });
                });
    
    
            function startCapture() {
              ref.current = setInterval(() => {
                const { yRatio, xRatio } = getXYRatio();
                /** 获取裁切框的位置 */
                const { left, top, width, height } = rectangle.getBoundingClientRect();
    
                const context = _canvas.getContext('2d');
                _canvas.width = width;
                _canvas.height = height;
    
                // void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
                context.drawImage(
                  video,
                  xRatio(left + window.scrollX),
                  yRatio(top + window.scrollY),
                  xRatio(width),
                  yRatio(height),
                  0,
                  0,
                  width,
                  height,
                );
    
                // 获取当前截图的base64编码
                const base64 = _canvas.toDataURL('image/jpeg');
                // 这里可以再根据场景做base64压缩
                // 每2秒调用OCR接口,上传base64到服务端进行识别
              }, 2000);
            }
    
            /** 清空定时器 */
            return () => clearInterval(ref.current);
        }, []);
    }
    
    

    注意: sx 、 sy 的值是相对根元素的,通过 getBoundingClientRect 拿到的 top 和 left 是相当于视口的,需要加上 scroll 的值。

    结语

    实际上 getUserMedia  在安卓和 MacOs 上跑起来几乎没有问题,但是社区中对于该 api 的讨论太少了,可能大部分人甚至不知道这个 api 的存在,在 ios 真机上进行调试时,一开始只展示有一帧,便静止了,报错不会给予开发者比较详细的提示,我一开始大部分时间都花在了研究 ios 端为什么无法正常调用该 api 。不过这种业务场景在 app 上应该是比较常见的,本文仅为h5该业务场景的实现方式。

    附一张最终效果图:

    H5实现自定义身份证拍照

    References

    1.iOS13 getUserMedia not working on chrome and edge stackoverflow.com/questions/6… bugs.webkit.org/show_bug.cg… It prevents ALL other browsers on iOS to offer video-conferencing, while Safari can => it's a nasty anti-competitive behaviour that will for sure be scrutinized by US House Antitrust Committee & EU Commission, and Apple should not accumulate evidence of evil conduct.

    2.MDN getUserMedia developer.mozilla.org/zh-CN/docs/…

    3.ios10+视频播放新策略 imququ.com/post/new-vi…


    起源地下载网 » H5实现自定义身份证拍照

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元