幾行程式碼教你解決微信生成海報及二維碼

全棧道路發表於2020-11-23

現如今圍繞微信生態相關開發已經非常常見,本期帶來如何通過 qrcode.js 實現微信內建瀏覽器動態生成二維碼並能夠長按識別 以及 通過 html2canvas 生成圖片並長按儲存

說幾個知識點

  • 微信長按彈出識別選項的原理
    • 微信客戶端檢測到使用者長按img標籤
    • 微信主動進行截圖並識別圖片,二維碼識別採用的是截圖而不是通過img標籤
    • 微信識別成功後執行相關操作
  • Base64
    • Base64是網路上最常見的用於傳輸8Bit位元組碼的編碼方式之一,Base64就是一種基於64個可列印字元來表示二進位制資料的方法
  • Blob
    • HTML5中的Blob物件與MySQL中的BLOB物件有區別,HTML5中的Blob物件除了存放二進位制資料外還可以設定這個資料的MINE型別,這相當於對檔案的儲存,其它很多二進位制物件也是從這個物件繼承的
  • canvas.toDataURL([type, encoderOptions])
    • type : 指定圖片型別,預設值 image/png
    • encoderOptions : 為 image/jpegimage/webp 型別的圖片設定圖片質量,取值0-1,超出則以預設值0.92替代
    • 作用: 通過canvas進行轉化圖片

準備工作

  • 結合微信規範明確需求
    • 微信img標籤通過src屬性可實現長按彈出選項(儲存至手機,圖片為二維碼的情況下會出現識別二維碼)
    • 二維碼圖片若為本地圖片或伺服器圖片(即不需要進行動態生成)只需要正常編寫程式碼即可實現
    • 微信針對內建瀏覽器內的頁面圖片有著自己的一套適應邏輯與規範,canvas的圖片和base64編碼格式的圖片在安卓與ios手機上會出現不同的問題
  • 確定實現方案
    • 本例採用第三方js庫實現生成二維碼
    • 針對生成的base64編碼的圖片微信無法長按識別需要在前端進行格式和image物件重新轉換
    • 生成的圖片彈窗展示,避免出現其他元素影響微信識別率

開發環境

  • 開發平臺
    • MacOS
  • 開發環境
    • Vue + node
  • 客戶端環境
    • Google Chrome
    • Wechat Webview

技術實現

本例的技術實現方案均在Vue專案環境下實現的

引入第三方js庫

  • 提供兩種引入方式,兩種方式是不同的js庫,方便大家選擇和使用

    • 本地引入 qrcode.js
    // qrcode.js官方GitHub文件: https://github.com/davidshimjs/qrcodejs
    <script src="static/js/qrcode.js"></script>
    
    • npm 引入 qrcodejs2
    npm install qrcodejs2
    import qrCode from 'qrcodejs2'
    

元件中呼叫

  • HTML

    <div class="qrcode-panel" id="qrcode"></div>
    
  • JS

    • 簡單呼叫
    new QRCode(document.getElementById('qrcode'), 'your content');
    // new QRCode(element, option)
    // element 顯示二維碼的元素或該元素的 ID
    // option  引數配置
    
    • 標準呼叫
    var qrcode = new QRCode(document.getElementById("qrcode"), {
    	text: "https://www.xxx.com?did=123456&id=123&userid=456",
    	width: 160, 		//影像寬度
    	height: 160,		// 影像高度
    	render: 'canvas',		// 生成格式(table 和 canvas)
    	colorDark : "#000000",		//前景色
    	colorLight : "#ffffff",		//背景色
    	correctLevel : QRCode.CorrectLevel.H    // 容錯級別
    });
    // 容錯級別,可設定為:
    QRCode.CorrectLevel.L(最大 7% 的錯誤能夠被糾正)
    QRCode.CorrectLevel.M(最大 15% 的錯誤能夠被糾正)
    QRCode.CorrectLevel.Q(最大 25% 的錯誤能夠被糾正)
    QRCode.CorrectLevel.H(最大 30% 的錯誤能夠被糾正)
    
    • 其他公共方法
    QRCode.makeCode(text) // 	設定二維碼內容
    QRCode.clear()  // 	清除二維碼
    

重置 Image 物件

  • 重置的原因是原JS生成的 image 和 canvas 物件無法在微信端長按識別

    var canvas = document.getElementsByTagName('canvas')[0];
    var img = this.convertCanvasToImage(canvas);
    document.getElementById("qrcode").append(img);
    
    convertCanvasToImage(canvas) {
    	//新建Image物件
    	var image = new Image();
    	// canvas.toDataURL 返回的是一串Base64編碼的URL
    	image.src = canvas.toDataURL("image/png");
    	image.id = 'qrcodeImg';
    	return image;
    }
    

後續細節處理

  • 至此,一個能夠滿足長按識別的動態二維碼已經生成,不繼續處理的話會有兩張二維碼,長按對比就能看出,qrcode.js 生成的二維碼長按無法識別,而經過重置之後的物件是可以實現此功能的。
  • 我的處理方式是兩個二維碼都保留,將二維碼圖片進行重新定位,將重置的二維碼圖片置於不能識別二維碼上層,不去頻繁操作DOM節點的顯示隱藏。
  • 生成的二維碼通過 append 的方式插入到dom節點中,在關閉操作時需要將之前生成的 canvasimage 去除

微信內建瀏覽器生成canvas圖片儲存

  • 上述教程可以實現動態生成二維碼進行儲存和長按識別,但是如果需要將HTML內容生成canvas儲存就存在問題了。
  • 針對儲存需要注意的幾個問題:
    • canvas禁止跨域
    • 安卓微信長按不能儲存base64圖片
    • 微信限制Blob型別圖片的儲存
    • 使用 canvas.toDataURL 繪製時的型別使用 image/jpeg 進行儲存

技術選型

  • 使用第三方JS庫 html2canvas 進行處理
  • 識別和生成原理:
    • 指令碼直接在使用者瀏覽器上擷取網頁或部分網頁的"螢幕截圖"
    • "螢幕截圖"基於DOM,因此它可能不是真實表示的100%準確,因為它沒有製作實際的螢幕截圖,而是根據頁面上可用的資訊構建螢幕截圖
  • 存在的問題:
    • 正是因為 html2canvas 不是基於真正的螢幕截圖去識別處理,所以脫離了文件流,或者文件流異常的元素會無法被擷取下來
    • html2canvas 只會擷取到目標元素寬高範圍內的內容
    • 對部分css樣式支援不好,相容性差的屬性列表
  • 一些可能需要的引數
    • useCORS : 是否嘗試使用CORS從伺服器載入影像
    • async : 是否非同步解析和呈現元素
    • scale : 用於渲染的比例。預設為瀏覽器裝置畫素比率window.devicePixelRatio
    • allowTaint : 是否允許畫布被汙染,被汙染的canvas是沒法使用toDataURL()轉base64流的,部分細節請 參考這裡
    • 更多 html2canvas 引數請點選這裡

引入第三方JS庫

  • 使用 html2canvas
import html2canvas from 'html2canvas'

元件中呼叫

  • HTML
<div class="html2canvas-conetent" ref="canvasContent">
	<img src="/static/images/canvas.jpg">
	<span>測試Title</span>
</div>
<button @click="showCanvas()">生成canvas圖片</button>
  • JS
    • 使用 html2canvas 推薦的promise方法
showCanvas() {
	let self = this;
	html2canvas(self.$refs.canvasContent).then(function(canvas) {
        self.imgUrl = canvas.toDataURL();
        self.showCanvasImg = true;
	});
}
// 非同步解析呼叫和呈現元素
showCanvas() {
	let self = this;
	html2canvas(self.$refs.canvasContent  {
		async: true
	}).then(canvas => {
		self.imgUrl = canvas.toDataURL();
		self.showCanvasImg = true;
	});
}

實現效果

原始碼地址

寫在最後

  • 如果你覺得這篇文章對你有益,煩請點贊以及分享給更多需要的人!

快到碗裡來!百度校招還有HC!甩簡歷來!

極速直接內推【位元組跳動】&【百度】&【猿輔導】&【京東】

歡迎關注微信公眾號【全棧道路】,獲取更多科技相關知識及免費書籍。

更多好文

冷門的HTML - tabindex 的作用

[萬字長文]百度和好未來面試經含答案

[前端面試]前端快取問題看這篇,讓面試官愛上你

記一次慘痛的Vue-cli + VueX + SSR經歷

[三分鐘小文]前端效能優化-HTML、CSS、JS部分

[三分鐘小文]前端效能優化-頁面載入速度優化

[三分鐘小文]前端效能優化-網路傳輸層優化

相關文章