使用 qrcodejs 生成二維碼的幾個問題

博客地址css

Preface

產品但願我這邊下載頁面加個二維碼,能夠掃描下載 APP,而且但願二維碼中有公司的 logo,很合理的需求,不過實現的時候依舊遇到了幾個問題,在此記錄下。html

Main

二維碼的實現邏輯我固然沒有這個時間去研究,直接用的 qrcodejs。官方給的 demo 是最簡單的版本,各類各樣的需求都是在 issues 裏找到的提示,彷佛這個庫已經好久沒有人去維護了,雖然 star 是不少。git

官網示例(改編)

<div id="qrcode" class="qrcode"></div>
.qrcode {
  width: 150px;
  height: 150px;
  border: 2px solid green;
  margin-top: 15px;
}
let qrcodeEl = document.getElementById('qrcode')
let qrcode = new QRCode(qrcodeEl, {
  text: 'https://avatars1.githubusercontent.com/u/23273077',
  width: 128,
  height: 128,
  colorDark: '#000000',
  colorLight: '#ffffff',
  correctLevel: QRCode.CorrectLevel.H
})

效果如圖:github

clipboard.png

尺寸控制

我給官網的示例加上了邊框,二維碼的尺寸和 js 裏的尺寸不同,這個缺點立馬就暴露出來了。canvas

我極可能是想生成的二維碼填滿傳入的 qrcode 元素的,可是這裏的 width 不支持 100%,更不支持 vw 這種尺寸單位了。固然,我能夠用 qrcode.offsetWidth 來解決這個問題,可是若是 qrcode 的尺寸後期會動態修改的話,那不仍是會有問題麼。微信

經 SO 的提示,發現了一個好方案,dom

.qrcode {
  width: 150px;
  height: 150px;
  border: 2px solid green;
  margin-top: 15px;
}
.qrcode canvas + img {
  width: 100%;
  height: 100%;
}

這樣就能夠了,不過仍然有個不足,就是二維碼有失真。經測試,只有傳入的尺寸和 qrcode 的尺寸同樣時,纔不會失真,因此傳入的尺寸仍是須要動態計算。異步

加 logo 的二維碼

qrcodejs 並無提供這個 API,issues 裏有人給了 demo,其實就是在原有元素上覆蓋一個 logo 就能夠了,雖然遮蓋了原有二維碼的一部分,可是實測並不影響掃描。不過我沒有進行大規模測試,可能會有必定的錯誤率。測試

<div id="qrcode" class="qrcode">
    <img src="https://avatars1.githubusercontent.com/u/23273077" class="qrcode__logo">
</div>
.qrcode {
  width: 150px;
  height: 150px;
  border: 2px solid green;
  margin-top: 15px;
  position: relative;
}

.qrcode canvas + img {
  width: 100%;
  height: 100%;
}
.qrcode__logo {
  width: 50px;
  height: 50px;
  border-radius: 10%;
  border: 1px solid #fff;
  position: absolute;
  margin: auto;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
}

效果如圖:spa

clipboard.png

檢測二維碼的生成

某些狀況下,我須要重用二維碼,在這種狀況下,我發現,二維碼的生成是異步的,譬如:

let qrcodeEl = document.getElementById('qrcode')
let qrcode = new QRCode(qrcodeEl, {
  text: 'https://avatars1.githubusercontent.com/u/23273077',
  width: 200,
  height: 200,
  colorDark: '#000000',
  colorLight: '#ffffff',
  correctLevel: QRCode.CorrectLevel.H
})
let qrcodeImg = document.querySelectorAll('.qrcode canvas+img')
console.log('qrcodeImg.src', qrcodeImg.src)
setTimeout(function() {
  console.log('qrcodeImg.src', qrcodeImg.src)
}, 1000)

第一個日誌就是空白的,第二個纔有 base64。搞笑的是,qrcodejs 也沒有給出回調或者通知告訴用戶何時生成完畢。

這個問題也是在 issues 裏找到的提示,關鍵點在於 MutationObserver

這個 API 不多在項目中用,由於不兼容性 ie11-,可是有時在幾千行代碼裏 debug 時會用,尤爲是我懷疑中間有代碼改了某個元素的屬性,確又找不到證據或者找不到哪段代碼時,會用這個來監測下。在這裏的用法以下:

let qrcodeEl = document.getElementById('qrcode')
let qrcode = new QRCode(qrcodeEl, {
  text: 'https://avatars1.githubusercontent.com/u/23273077',
  width: 200,
  height: 200,
  colorDark: '#000000',
  colorLight: '#ffffff',
  correctLevel: QRCode.CorrectLevel.H
})
let qrcodeImg = document.querySelector('.qrcode canvas+img')
listenQrcodeSrc()
function listenQrcodeSrc() {
  var observeConfig = { attributes: true }
  var observeCb = function(mutationsList, observer) {
    mutationsList.forEach(function(mutation) {
      if (
        mutation.type.toLowerCase() === 'attributes' &&
        mutation.attributeName.toLowerCase() === 'src'
      ) {
        console.log('qrcodeImg src done!', mutation.target.src)
        observer.disconnect()
      }
    })
  }
  if (typeof MutationObserver !== 'undefined') {
    var observer = new MutationObserver(observeCb)
    observer.observe(qrcodeImg, observeConfig)
  }
}

微信中多個二維碼在一塊兒識別錯誤

這個問題,我也遇到了,根據網友的提示,微信是截屏識別的,因此會出現這種問題。我測試的結果是,左右兩個,永遠識別的右邊的那個。網上有好幾種方案:

  • 調透明度和層級

最初嘗試過,結果發現失敗,等到成功的時候,透明度已經小於 0.5 了,視覺差別太明顯,因此放棄了這個方案。

  • 替換二維碼

最終採起的是這個,這個也有問題,就是用戶會看到二維碼變化的過程,除非你把多個二維碼作得很像。

假設,咱們要顯示兩個二維碼,所謂替換二維碼,其實也就是在多個 img.src 屬性裏切換,能夠把實際的二維碼保存在 data-real-src 屬性裏,而後在用戶 touchstart 事件中,替換另外一個 imgsrc 爲當前按下的這個,而後在 touchend 事件中再改回來,由於原來的地址都保存在 data-real-src 屬性裏。

這裏就用到了前面提到的檢測 src 屬性來判斷 qrcode 生成完畢,不然一開始直接把 src 屬性賦給 data-real-src 屬性,就是空白。

示例代碼(這裏代碼跟前面脫節了,dom 是另外的結構,僅做爲示例代碼):

//* pubMethods 是相似 jq 的一些 API 的彙總對象
var qrcodeImgs = pubMethods.$('.download__qrcode-box canvas+img')
listenQrcodeSrc()
var downloadBox = pubMethods.$('.download')[0]
downloadBox.addEventListener('touchstart', changeQrcodeSrcToOne)
downloadBox.addEventListener('touchend', changeQrcodeSrcBack)
downloadBox.addEventListener('touchcancel', changeQrcodeSrcBack)

function listenQrcodeSrc() {
  var observeConfig = { attributes: true }
  var observeCb = function(mutationsList, observer) {
    mutationsList.forEach(function(mutation) {
      if (
        mutation.type.toLowerCase() === 'attributes' &&
        mutation.attributeName.toLowerCase() === 'src'
      ) {
        mutation.target.setAttribute('data-real-src', mutation.target.src)
        observer.disconnect()
      }
    })
  }
  qrcodeImgs.forEach(function(ele) {
    if (typeof MutationObserver !== 'undefined') {
      var observer = new MutationObserver(observeCb)
      observer.observe(ele, observeConfig)
    }
  })
}
function changeQrcodeSrcToOne(event) {
  var target = event.target
  var getQrcodeBox = pubMethods.closest(
    target,
    '.download__qrcode-box',
    downloadBox
  )
  if (getQrcodeBox) {
    var targetImg = qrcodeImgs.filter(function(ele) {
      return getQrcodeBox.contains(ele)
    })[0]
    qrcodeImgs.forEach(function(ele) {
      ele.src = targetImg.getAttribute('data-real-src')
    })
  }
}
function changeQrcodeSrcBack(event) {
  qrcodeImgs.forEach(function(ele) {
    ele.src = ele.getAttribute('data-real-src')
  })
}

Ending

Reference

相關文章
相關標籤/搜索