在現代 Web 應用開發中,二維碼的應用越來越廣泛,從電子票務到信息傳遞,它都扮演著重要角色。本文將分享如何在 Vue 項目中,結合QRCode
庫實現動態二維碼的生成、與背景圖合成以及圖片下載功能,打造一個完整且實用的二維碼展示模塊,最終效果兼顧美觀與交互性。
項目背景與需求分析
在我們的五龍山野生動物園票務項目中,需要為用戶提供電子門票二維碼展示頁面。該頁面不僅要展示清晰的門票二維碼,還需結合動物園特色背景圖,增強視覺體驗。同時,為方便用戶保存門票,需實現長按二維碼保存合成圖片的功能。這就涉及到二維碼生成、圖片合成以及用戶交互邏輯的實現。
技術棧選擇
本項目基于 Vue.js 框架進行開發,結合QRCode
庫用于二維碼的生成,利用原生 JavaScript 的 Canvas API 完成圖片合成與繪制,確保功能的高效與穩定。
代碼實現詳解
1. 樣式設計(<style>
部分)
* {margin: 0;padding: 0;
}.yard_box {width: 100%;height: 100vh;background-image: url('../assets/yard_back.png');background-size: cover;background-repeat: no-repeat;background-position: center;position: relative;
}.top_title {width: 100%;height: 50px;display: flex;
}.return_icon {width: 40px;height: 40px;
}.return_box {width: 40px;height: 40px;background-color: rgb(70, 58, 58);border-radius: 50%;opacity: 0.7;margin-left: 2%;margin-top: 2%;
}.qr-canvas {width: 200px;height: 200px;position: absolute;left: 51%;bottom: 180px;/* 調整二維碼在頁面中的位置 */transform: translateX(-50%);cursor: pointer;transition: transform 0.1s;
}.qr-canvas:active {transform: translateX(-50%) scale(0.98);
}.save-hint {position: absolute;bottom: 50px;left: 0;right: 0;text-align: center;color: white;font-size: 14px;text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
}
?
樣式部分首先通過*
選擇器重置默認樣式,消除內外邊距。yard_box
類為整個頁面設置了全屏背景圖,營造沉浸式視覺效果。top_title
與return_box
實現了返回按鈕的布局與樣式,qr-canvas
類定義了二維碼畫布的初始位置、大小及交互效果,通過active
偽類實現按壓時的縮放動效,提升交互體驗。
2. 模板搭建(<template>
部分)
<template><div class="yard_box"><div class="top_title" @click="$router.go(-1)"><div class="return_box"><img src="../assets/left.svg" alt="" class="return_icon"></div></div><!-- 顯示二維碼的畫布 --><canvas ref="qrCanvas" @mousedown="startLongPress" @mouseup="cancelLongPress" @mouseleave="cancelLongPress"@touchstart="startLongPress" @touchend="cancelLongPress" @touchcancel="cancelLongPress"class="qr-canvas"></canvas></div>
</template>
模板中,外層yard_box
包裹頁面所有元素,top_title
綁定點擊事件實現返回上一頁功能。<canvas>
標簽用于繪制二維碼,同時綁定了鼠標和觸摸事件,以兼容 PC 端與移動端的長按操作,為后續的保存功能提供交互基礎。
3. 邏輯實現(<script>
部分)
import QRCode from 'qrcode';
import yardBack from '@/assets/yard_back.png'; // 使用ES模塊導入export default {data() {return {ticketId: '35',longPressTimer: null,longPressDuration: 1000, // 長按時間閾值(1秒)backgroundImage: new Image() // 用于合成圖片的背景圖};},methods: {async loadBackgroundImage() {return new Promise((resolve) => {this.backgroundImage.src = yardBack; // 使用導入的圖片路徑this.backgroundImage.onload = resolve;});},// 生成二維碼generateQRCode() {const canvas = this.$refs.qrCanvas;QRCode.toCanvas(canvas, this.ticketId, {width: 200,margin: 2}, (error) => {if (error) console.error('二維碼生成失敗:', error);});},// 開始長按startLongPress() {this.longPressTimer = setTimeout(() => {this.saveMergedImage();}, this.longPressDuration);},// 取消長按cancelLongPress() {if (this.longPressTimer) {clearTimeout(this.longPressTimer);this.longPressTimer = null;}},// 創建并保存合并后的圖片async saveMergedImage() {// 創建臨時Canvas用于合成const mergedCanvas = document.createElement('canvas');const ctx = mergedCanvas.getContext('2d');// 設置Canvas尺寸與背景圖一致mergedCanvas.width = this.backgroundImage.width;mergedCanvas.height = this.backgroundImage.height;// 1. 繪制背景圖ctx.drawImage(this.backgroundImage, 0, 0, mergedCanvas.width, mergedCanvas.height);// 2. 計算白色區域的位置和大小(需要根據實際背景圖調整)const whiteArea = {x: mergedCanvas.width * 0.2, // 白色區域左側位置(20%寬度處)y: mergedCanvas.height * 0.45, // 白色區域頂部位置(60%高度處)width: mergedCanvas.width * 0.6, // 白色區域寬度(60%寬度)height: mergedCanvas.height * 0.3 // 白色區域高度(30%高度)};// 3. 計算二維碼在白色區域中的合適大小和位置const qrCanvas = this.$refs.qrCanvas;const qrMaxSize = Math.min(whiteArea.width, whiteArea.height) * 0.8; // 二維碼最大尺寸(白色區域的80%)const qrSize = Math.min(420, qrMaxSize); // 不超過200px// 計算居中位置const qrX = whiteArea.x + (whiteArea.width - qrSize) / 2;const qrY = whiteArea.y + (whiteArea.height - qrSize) / 2;// 繪制二維碼ctx.drawImage(qrCanvas, qrX, qrY, qrSize, qrSize);// 4. 添加文字(調整到二維碼上方)const textY = qrY - 30;ctx.font = 'bold 40px Arial';ctx.fillStyle = 'white';ctx.textAlign = 'center';ctx.fillText('', mergedCanvas.width / 2, textY);// 保存圖片const link = document.createElement('a');link.download = `五龍山野生動物園門票-${this.ticketId}.png`;link.href = mergedCanvas.toDataURL('image/png');document.body.appendChild(link);link.click();document.body.removeChild(link);}},async mounted() {await this.loadBackgroundImage();await this.generateQRCode();}
};
在JavaScript
邏輯部分,首先導入QRCode
庫和背景圖片資源。data
中定義了門票 ID、長按計時器、長按閾值以及背景圖片對象。
loadBackgroundImage
方法通過 Promise 封裝圖片加載過程,確保圖片加載完成后再進行后續操作。generateQRCode
方法利用QRCode
庫將門票 ID 生成到指定的<canvas>
上。
長按相關方法startLongPress
和cancelLongPress
分別用于啟動長按計時和取消計時,當長按達到設定閾值時,觸發saveMergedImage
方法。
saveMergedImage
方法是核心邏輯,創建新的<canvas>
用于合成圖片,先繪制背景圖,再計算并繪制白色區域、二維碼,最后添加文字。通過創建<a>
標簽,利用toDataURL
方法將合成后的圖片轉換為 URL,實現圖片下載功能。mounted
鉤子函數確保頁面加載時完成背景圖加載和二維碼生成。
效果展示與優化方向
最終實現的頁面,背景圖與二維碼完美融合,用戶長按二維碼即可保存包含背景、二維碼和文字信息的合成圖片。后續優化可以考慮:
- 動態數據綁定:將
ticketId
改為動態獲取,實現不同門票二維碼的展示。 - 樣式優化:根據實際背景圖調整白色區域和文字樣式,增強視覺美感。
- 錯誤處理:完善二維碼生成和圖片合成過程中的錯誤提示,提升用戶體驗。
通過以上步驟,我們成功在 Vue 項目中實現了一個功能完整、交互良好的二維碼合成與下載模塊。希望本文能為你的前端開發實踐帶來啟發,在實際項目中靈活運用相關技術!