小程序Canvas實戰(zhàn):輕松生成自定義二維碼
在小程序開發(fā)中,二維碼是極其重要的功能組件: 用戶分享推廣的關(guān)鍵媒介 活動頁面入口的便捷方式 支付、登錄等安全驗證手段 商品、服務(wù)的快速識別通道 小程序自帶的二維碼生成API雖然方便,但存在樣式單一、尺寸固定等問題。使用Canvas繪制二維碼則提供了無限的自定義可能性! 使用第三方庫 通過Canvas繪制基礎(chǔ)二維碼 添加自定義樣式和特效 實現(xiàn)二維碼保存功能 安裝二維碼生成庫: 構(gòu)建npm模塊(小程序開發(fā)者工具中操作) 為解決Canvas在高分辨率設(shè)備上模糊的問題: 小程序中Canvas是原生組件,層級最高,可通過以下方案解決: 使用 在不需要交互時隱藏Canvas 使用圖片替代Canvas顯示二維碼 Canvas繪制不顯示問題 確保在 檢查Canvas ID是否正確 確認Canvas在頁面中可見 保存圖片權(quán)限問題 安卓設(shè)備模糊問題 使用高清繪制技巧(像素比縮放) 避免在二維碼中使用過細的線條 適當(dāng)提高容錯級別 通過本文介紹的方法,你可以在小程序中實現(xiàn): 自定義樣式的二維碼生成 Logo和裝飾元素的添加 二維碼保存與分享功能 豐富的視覺效果增強 Canvas繪制二維碼不僅提供了更大的靈活性,還能讓你的小程序在視覺效果上脫穎而出。趕緊嘗試實現(xiàn)屬于你的個性二維碼吧!weapp-qrcode
生成二維碼數(shù)據(jù)npm install weapp-qrcode --save
<view class="container">
<view class="canvas-container">
<canvas
canvas-id="qrcodeCanvas"
style="width: 300px; height: 300px;"
class="qrcode-canvas"
></canvas>
</view>
<view class="controls">
<input
type="text"
placeholder="輸入二維碼內(nèi)容"
bindinput="onInputChange"
value="{{text}}"
class="input-box"
/>
<view class="style-controls">
<view class="color-picker">
<text>主色:</text>
<input type="color" bindinput="onColorChange" value="{{darkColor}}" />
</view>
<view class="color-picker">
<text>背景色:</text>
<input type="color" bindinput="onBgColorChange" value="{{lightColor}}" />
</view>
</view>
<button bindtap="toggleLogo" class="btn">
{{showLogo ? '移除Logo' : '添加Logo'}}
</button>
<button bindtap="saveImage" class="btn save-btn">保存到相冊</button>
</view>
</view>
.container {
display: flex;
flex-direction: column;
align-items: center;
padding: 20rpx;
background: #f5f5f5;
min-height: 100vh;
}
.canvas-container {
background: white;
border-radius: 16rpx;
padding: 20rpx;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
margin-bottom: 40rpx;
}
.controls {
width: 100%;
max-width: 600rpx;
}
.input-box {
width: 100%;
height: 80rpx;
background: white;
border-radius: 8rpx;
padding: 0 20rpx;
margin-bottom: 30rpx;
box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.05);
}
.style-controls {
display: flex;
justify-content: space-between;
margin-bottom: 30rpx;
}
.color-picker {
display: flex;
align-items: center;
background: white;
padding: 10rpx 20rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.05);
}
.color-picker text {
margin-right: 10rpx;
font-size: 28rpx;
}
.btn {
width: 100%;
height: 80rpx;
line-height: 80rpx;
background: #07c160;
color: white;
border-radius: 8rpx;
margin-bottom: 20rpx;
font-size: 32rpx;
}
.save-btn {
background: #1a6bff;
margin-top: 30rpx;
}
.qrcode-canvas {
border: 1rpx solid #eee;
}
import QRCode from 'weapp-qrcode';
Page({
data: {
text: 'https://example.com',
darkColor: '#000000',
lightColor: '#ffffff',
showLogo: false
},
onReady() {
this.drawQRCode();
},
onInputChange(e) {
this.setData({ text: e.detail.value });
this.drawQRCode();
},
onColorChange(e) {
this.setData({ darkColor: e.detail.value });
this.drawQRCode();
},
onBgColorChange(e) {
this.setData({ lightColor: e.detail.value });
this.drawQRCode();
},
toggleLogo() {
this.setData({ showLogo: !this.data.showLogo });
this.drawQRCode();
},
drawQRCode() {
const { text, darkColor, lightColor, showLogo } = this.data;
// 獲取Canvas上下文
const ctx = wx.createCanvasContext('qrcodeCanvas', this);
// 清空畫布
ctx.clearRect(0, 0, 300, 300);
// 創(chuàng)建二維碼
const qrcode = new QRCode(ctx, {
text: text || 'https://example.com',
width: 300,
height: 300,
colorDark: darkColor,
colorLight: lightColor,
correctLevel: QRCode.CorrectLevel.H
});
// 繪制二維碼
qrcode.make();
// 添加Logo
if (showLogo) {
ctx.drawImage('/images/logo.png', 125, 125, 50, 50);
}
// 添加外框裝飾
ctx.setStrokeStyle(darkColor);
ctx.setLineWidth(4);
ctx.strokeRect(10, 10, 280, 280);
// 添加中心裝飾
ctx.beginPath();
ctx.arc(150, 150, 30, 0, 2 * Math.PI);
ctx.setFillStyle(lightColor);
ctx.fill();
// 繪制到Canvas
ctx.draw();
},
saveImage() {
wx.canvasToTempFilePath({
canvasId: 'qrcodeCanvas',
success: (res) => {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
wx.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
console.error('保存失敗', err);
wx.showToast({
title: '保存失敗,請檢查權(quán)限',
icon: 'none'
});
}
});
},
fail: (err) => {
console.error('生成臨時文件失敗', err);
}
});
}
});
// 獲取設(shè)備像素比
const dpr = wx.getSystemInfoSync().pixelRatio;
// 設(shè)置Canvas實際尺寸
const canvasWidth = 300 * dpr;
const canvasHeight = 300 * dpr;
// 設(shè)置Canvas顯示尺寸
this.setData({
canvasStyle: `width: 300px; height: 300px;`
});
// 四個容錯級別可選
QRCode.CorrectLevel = {
L: 1, // 約可糾錯7%的數(shù)據(jù)
M: 0, // 約可糾錯15%的數(shù)據(jù)
Q: 3, // 約可糾錯25%的數(shù)據(jù)
H: 2 // 約可糾錯30%的數(shù)據(jù)(最高)
};
cover-view
覆蓋需要顯示的元素// 在二維碼周圍添加旋轉(zhuǎn)動畫
ctx.beginPath();
ctx.arc(150, 150, 160, 0, 2 * Math.PI);
ctx.setStrokeStyle('#ff6b6b');
ctx.setLineWidth(3);
ctx.setLineDash([10, 5]);
ctx.stroke();
// 添加旋轉(zhuǎn)動畫
this.animateCircle(ctx);
// 創(chuàng)建漸變對象
const gradient = ctx.createLinearGradient(0, 0, 300, 300);
gradient.addColorStop(0, '#ff9a9e');
gradient.addColorStop(1, '#fad0c4');
// 使用漸變填充
qrcode.colorDark = gradient;
// 繪制背景圖
ctx.drawImage('/images/background.jpg', 0, 0, 300, 300);
// 在背景上繪制半透明覆蓋層
ctx.setFillStyle('rgba(255,255,255,0.7)');
ctx.fillRect(0, 0, 300, 300);
// 在上面繪制二維碼
qrcode.make();
onReady
或之后的生命周期調(diào)用繪制方法// 在保存前檢查權(quán)限
wx.getSetting({
success: (res) => {
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success: () => this.saveImage(),
fail: () => wx.openSetting()
});
} else {
this.saveImage();
}
}
});