博客地址css
產品但願我這邊下載頁面加個二維碼,能夠掃描下載 APP,而且但願二維碼中有公司的 logo,很合理的需求,不過實現的時候依舊遇到了幾個問題,在此記錄下。html
二維碼的實現邏輯我固然沒有這個時間去研究,直接用的 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
我給官網的示例加上了邊框,二維碼的尺寸和 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
的尺寸同樣時,纔不會失真,因此傳入的尺寸仍是須要動態計算。異步
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
某些狀況下,我須要重用二維碼,在這種狀況下,我發現,二維碼的生成是異步的,譬如:
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
事件中,替換另外一個 img
的 src
爲當前按下的這個,而後在 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') }) }