如何優雅的在 H5 網頁中實現掃碼功能

之前在做的項目中需要實現一個掃碼錄入的功能,具體效果就是在點擊掃碼按鈕後調取攝像頭,掃描條形碼將解碼後的數據填入到輸入框中。相信這個功能在日常的手機使用中,是非常常用的,下面來看看我是如何一步一步踩坑實現的吧

💡 內容搶先看

  1. 採用 Quagga 庫實現遇到的問題和瓶頸

  2. 接入微信 SDK 的踩坑指北

  3. 最終解決方案

一、掃碼初探 Quagga

第一次接觸到掃碼的業務,一切都是全新的,也不知道從哪裏開始,先到各個社區搜索了一下

當時的搜索詞大概是這樣,得到結果就是利用 HTML5 中提供的 API 去實現,然後又看到了 QuaggabarCode 等庫,在 npm 上查看了官方描述後,我覺得 Quagga 這個庫可能更符合這個項目的需求,因此對 Quagga 進行了一番研究 Quagga

這個庫的使用並不難,首先需要引入 Quagga 這個庫

yarn add quagga

由於我是在 React 項目下開發的,因此我們需要在 returnJSX 內 添加一個 DOM 節點,用來定製攝像頭打開攝像頭影像的投放區域

代碼中的 yourElement  節點就是用來做攝像頭的影像投放的

接下來,需要開始使用 Quagga ,大概就是調一下方法,配置一下解碼方式,掃碼區域,然後就能喚起攝像頭,進行掃碼,再處理一下掃碼結果。由於初學,對它具體的代碼書寫還不是很熟悉,因此看老外敲了一晚的代碼,並在一個大佬的 github 倉庫內,找到了一個開源的掃碼錄入項目 👇👇👇

看了看它的實現代碼,然後開始了我的踩坑

首先我們需要初始化 Quagga

需要定製一下我們的解碼方式,對於我們需要掃描的碼是什麼類型的,可以找個檢測網站測一下,我的是 Code39 類型的

Quagga.init({
  inputStream: {
    name: "Live",
    type: "LiveStream", // 方式
    constraints: {
      width: '790',
      height: '490'
    }, // 解碼區域
    numberOfWorkers: navigator.hardwareConcurrency,
    target: document.querySelector('#barcodeScan') // 影像輸出到的節點
  },
  locate: true,
  decoder: {
    readers: ["code_39_reader"] // 解碼方式
  }
}function (err) {
  if (err) {
    return 
  }
  Quagga.start() // 開啓
})
Quagga.onDetected(this.onDetect)

當成功掃碼後會調用 onDetected 方法,這就有點像生命週期一樣,都是定義好的,我們可以在這個函數里接收到返回來的 result 獲取到 codeResult 掃描結果

onDetect(res){
  // console.log(res.codeResult.code)
  Quagga.stop()
  Quagga.offProcessed()
  console.log(res.codeResult);
}

然後我們就可以開始瘋狂的掃碼,測試,經過了一晚上的測試,我發現它並不能像別人視頻中那樣流暢準確的進行解碼,基本上輸出的結果沒幾個是對的上的,因此此路行不通,開始萌生了放棄的想法,後來發現可以直接調取微信的掃碼功能,來實現我們的需求,因此開始了微信 SDK 踩坑之路

二、大戰微信 SDK

在經歷了 quagga 的失利後,開始了搗鼓接入微信 SDK 來實現,首先我們需要引入 weixin-js-sdk ,這樣我們就可以使用一些 wx 官方 API,比如這裏需要使用到 scanQRcode

在這裏我們調用這個 API,它接收一個配置對象,具體可以看官方文檔

比較重要的就是 needResult 它決定掃碼的結果由誰來處理,如果是 0 ,則由微信處理,例如掃取一個快遞碼,它在掃描結束後會跳轉到對應的頁面去,這不是我們想要的,因此設置爲 1,會直接返回掃描的結果,我們可以在 success 回調中獲取到這個結果 resultStr ,這個 resultStr 是一個數組,第一項是掃碼的類型,第二項是解碼後的值,因此我們處理一下

wx.scanQRCode({
    needResult: 1, // 默認爲0,掃描結果由微信處理,1則直接返回掃描結果,
    scanType: ["qrCode""barCode"], // 可以指定掃二維碼還是一維碼,默認二者都有
    success: function (res) {
        let result = res.resultStr.split(',')[1]; // 當needResult 爲 1 時,掃碼返回的結果
    },
    fail: function (err) {
        console.log('error');
        console.log(err);
    }
})

這樣我們一個簡單的掃碼功能就寫好了,真是很方便,只需要綁定到事件處理回調即可被調用。真的是這麼簡單嗎?

由於我們使用的是微信開放能力,我們需要進行配置,採用官方提供的 wx.config

需要配置我們的 appId,以及一個簽名還需要配置我們需要使用的 API

wx.config({
    debug: false, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。
    appId, // 必填,公衆號的唯一標識
    timestamp, // 必填,生成簽名的時間戳
    nonceStr, // 必填,生成簽名的隨機串
    signature,// 必填,簽名
    jsApiList: ["scanQRCode"] // 必填,需要使用的JS接口列表
});

簽名這些配置項,需要後臺哥哥返回,我們需要向後端傳遞我們當前的 url,用來生成掐滅

注意:一定是需要動態的,寫死了會有 bug

這樣我們調用接口,後端返回幾個參數即可

const data = await getWxKey({ url: window.location.href.split('#')[0] })

這裏有幾個需要注意的問題

  1. 調取微信 API 需要使用 access_token 這個需要後端去獲取,怎麼解決我也不清楚,應該是通過 微信公衆號的 addId 去申請的

  2. 向後端傳遞的 url ,是需要通過動態獲取的,不然可能會有 invalid signature 簽名錯誤的情況

在代碼層面上,我們能做的就是這些了,前端的代碼邏輯沒有很多,都是 API 調用,完整代碼如下

const openCamera = async () ={
    const data = await getWxKey({ url: window.location.href.split('#')[0] })
    const { appId, timestamp, nonceStr, jsKey: signature } = data.data
    wx.config({
        debug: false, // 開啓調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時纔會打印。
        appId, // 必填,公衆號的唯一標識
        timestamp, // 必填,生成簽名的時間戳
        nonceStr, // 必填,生成簽名的隨機串
        signature,// 必填,簽名
        jsApiList: ["scanQRCode"] // 必填,需要使用的JS接口列表
    });
    wx.ready(() ={
        wx.scanQRCode({
            needResult: 1, // 默認爲0,掃描結果由微信處理,1則直接返回掃描結果,
            scanType: ["qrCode""barCode"], // 可以指定掃二維碼還是一維碼,默認二者都有
            success: function (res) {
                let result = res.resultStr.split(',')[1]; // 當needResult 爲 1 時,掃碼返回的結果
                form.setFieldsValue({ orderNumber: result })
            },
            fail: function (err) {
                console.log(err);
            }
        })
    })
}

但是寫到這裏,我們的掃碼功能仍然是不可用的,我們還需要在微信公衆平臺來配置我們的接口域名,不然得到的會是 👇👇👇

我們需要在微信公衆平臺配置 JS 接口安全域名

這樣我們的問題就能迎刃而解,在配置安全域名的時候,注意訪問的域名不要啓動代理,不然會綁定不成功


至此我們的需求得以完成,文章可能看起來很容易,但是實際操作起來會用很多各種各樣的問題,希望這篇文章能夠幫助到有類似需求的你

小 tips:遇到問題的時候可以嘗試在微信交流平臺找答案,或者可以查看官方文檔

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/vWbbs4vdG8Uy4zoNH-pTaQ