聊聊H5浏览器实现扫一扫

我们开发了一款H5产品,原本是由小程序跳转进入的,结果客户因为域名未备案原因,导致产品无法在移动端使用。继而我们将程序嵌入APP,奈何客户方APP里面居然米有提供扫一扫接口导致产品无法使用。于是自己调研了一下怎样不借助平台自己实现扫一扫功能。找了相当多的资料,结果在某哥的文章中找到了解决方法,点此可看(掘金),在此也谢谢这位大神的文章,让我脱离苦海。接下来我讲下我的实现:

我们移动端用的是react

第一步引入插件zxing

cnpm i -S @zxing/library@0.15.1

此处注意,一定要使用0.15.x或者0.16.0版本,其他版本会报错

第二步,测试页面代码

import React, {useState, useEffect} from 'react';
import { Toast, Button } from 'antd-mobile';
import { BrowserQRCodeReader } from '@zxing/library';

// 此方法是为了兼容ios无法获取设备ID而写的
async function getVedioPermission(){
    let m = await navigator.mediaDevices.getUserMedia({video:true, audio:true}).catch(function(){Promise.resolve(null)})
    if(!m){
      Toast.fail('获取权限失败,label 和 deviceId不显示',3);
    }
    let d = await navigator.mediaDevices.enumerateDevices(); 
    // let str = ""
    let did = '';
    d.forEach(function(item) {
      // str+=`kind: ${item.kind};\ngroupId: ${item.groupId}; \ndeviceId: ${item.deviceId}; \nlabel: ${item.label};\n`;
      if (item.kind === 'videoinput') {
        did = item.deviceId; // 获取最后一个视频设备的id(原则上最后一个是后置摄像头)
      }
    })
    // alert(str); // 查看系统所有设备列表
    return Promise.resolve(did);
}

function initReader(){
  const codeReader = new BrowserQRCodeReader();
  return codeReader.getVideoInputDevices()
  .then(async videoInputDevices => {
    if (videoInputDevices.length <= 0){
      throw Error('没找到摄像头啊');
    }
    // 双摄的时候采用后置,否则采用第一个
    let idid = videoInputDevices[videoInputDevices.length -1].deviceId || videoInputDevices[0].deviceId;
    if (!idid) {
      idid = await getVedioPermission();
    }
    return {
        videoDeviceID: idid,
        codeReader
    }
  })
  .catch(err => {
    console.log(err, 15);
  })
}

function App() {

  const [reader, setReader] = useState();
  const [deviceID, setDeviceID] = useState();

  const [message, setMessage] = useState();
  const [result, setResult] = useState();

  useEffect(() => {
    (async function (){
      if(!reader){
        const {videoDeviceID, codeReader} = await initReader();
        setReader(codeReader);
        setDeviceID(videoDeviceID);
      }
    })()
  }, [reader]);

  const decode = (codeReader, selectedDeviceId) => {
    codeReader.decodeFromInputVideoDevice(selectedDeviceId, 'video')
    .then((result) => {
      //console.log(result);
      setResult(result.text);
    }).catch((err) => {
      alert(JSON.stringify(err));
      setMessage(err.toString());
    })
  }

  return <div className="App">
  
    <div className="container" style={{textAlign:'center', width:'100%'}}>
      <div>
        <video id="video" width="200" height="200" style={{border: '1px solid gray', margin:'30px'}} />
        <Button style={{margin:'30px'}} size="lg" color="primary" onClick={() => decode(reader, deviceID)}>扫一扫</Button>
      </div>
    </div>
    <div>{result}</div>
    <div>{message}</div>
  </div>
}

export default App;

第三步,启动服务,切忌PC端测试的话要用localhost访问,否则无权限;

发布到服务器上使用的话,谨记需要https协议,否则也无权限访问。

不过根据资料显示IOS需要在safari版本在11之后的才支持,我没有测试

上一篇:说一下Hooks,为什么引入Hook,常见Hook的用法


下一篇:java中两种添加监听器的策略