フロントエンドでユーザーの個人名刺用QRコードを動的に生成し、ダウンロード機能を提供する要件がありました。様々な解決策を検討した結果、実装を完了させたので、その技術的なアプローチを記録します。
機能概要
- QRコードの密度を調整可能
- カスタムカラーでのQRコード生成
- 生成したQRコードのダウンロード機能
- シンプルで理解しやすい実装構造
実装アプローチ
1. QRコード生成コンポーネントの作成
<template>
<div class="qr-container">
<div class="qr-display" v-if="showQR">
<canvas ref="qrCanvas" :width="canvasSize" :height="canvasSize"></canvas>
<button @click="closeQR" class="close-btn">✕</button>
</div>
<div class="qr-controls">
<h3>{{ qrTitle }}</h3>
<p>{{ qrDescription }}</p>
<button @click="generateQR" class="generate-btn">QRコードを生成</button>
</div>
</div>
</template>
<script>
import QRCode from 'qrcode'
export default {
name: 'QRCodeGenerator',
data() {
return {
showQR: false,
qrTitle: 'QRコード生成',
qrDescription: 'ボタンクリックでQRコードを生成します',
canvasSize: 256,
qrData: '',
qrDarkColor: '#000000',
qrMargin: 2
}
},
methods: {
async generateQR() {
this.qrData = this.generateRandomString(32)
this.qrDarkColor = this.generateRandomColor()
try {
const canvas = this.$refs.qrCanvas
await QRCode.toCanvas(canvas, this.qrData, {
width: this.canvasSize,
margin: this.qrMargin,
color: {
dark: this.qrDarkColor,
light: '#FFFFFF'
},
errorCorrectionLevel: 'H'
})
this.showQR = true
} catch (err) {
console.error('QRコード生成エラー:', err)
}
},
generateRandomString(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))
}
return `https://example.com/${result}`
},
generateRandomColor() {
const hex = '0123456789ABCDEF'
let color = '#'
for (let i = 0; i < 6; i++) {
color += hex[Math.floor(Math.random() * 16)]
}
return color
},
closeQR() {
this.showQR = false
},
async downloadQR() {
const canvas = this.$refs.qrCanvas
canvas.toBlob((blob) => {
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `qrcode_${Date.now()}.png`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
URL.revokeObjectURL(url)
})
}
}
}
</script>
<style>
.qr-container {
position: relative;
padding: 20px;
}
.qr-display {
position: absolute;
top: 0;
left: 0;
background: rgba(255, 255, 255, 0.95);
border: 1px solid #ddd;
border-radius: 8px;
padding: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.close-btn {
position: absolute;
top: 5px;
right: 5px;
width: 24px;
height: 24px;
border-radius: 50%;
border: none;
background: #f0f0f0;
cursor: pointer;
font-size: 16px;
}
.generate-btn {
margin-top: 20px;
padding: 10px 20px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
canvas {
display: block;
}
</style>
2. ダウンロード機能の実装
// QRCodeGeneratorコンポーネントに追加するメソッド
methods: {
async downloadQR() {
const canvas = this.$refs.qrCanvas
if (!canvas) return
try {
// CanvasをBlobに変換
const blob = await new Promise((resolve) => {
canvas.toBlob(resolve, 'image/png')
})
// ダウンロードリンクを作成
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `qrcode_${Date.now()}.png`
// ダウンロード実行
document.body.appendChild(link)
link.click()
// 後処理
document.body.removeChild(link)
URL.revokeObjectURL(url)
} catch (error) {
console.error('ダウンロードエラー:', error)
}
}
}
3. Canvasを直接使用した高度な実装(ミニプログラム向け)
// ミニプログラムでのCanvas使用例
export default {
data() {
return {
qrCanvasSize: 200,
showQR: false,
qrText: '',
qrColor: '#000000'
}
},
methods: {
generateQRWithCanvas() {
const systemInfo = wx.getSystemInfoSync()
const canvasWidth = this.qrCanvasSize / 750 * systemInfo.windowWidth
const ctx = wx.createCanvasContext('qrCanvas')
// QRコードデータの準備
this.qrText = `https://company.com/share?${this.generateParams()}`
// QRコード描画
this.drawQRCode(ctx, {
text: this.qrText,
width: canvasWidth,
height: canvasWidth,
foreground: this.qrColor
})
this.showQR = true
},
generateParams() {
const timestamp = Date.now()
const randomId = Math.random().toString(36).substr(2, 9)
return `id=${randomId}&t=${timestamp}`
},
drawQRCode(ctx, options) {
// drawQrcodeライブラリを使用した描画処理
drawQrcode({
width: options.width,
height: options.height,
canvasId: 'qrCanvas',
text: options.text,
foreground: options.foreground,
callback: () => {
ctx.draw()
}
})
},
saveCanvasImage() {
wx.canvasToTempFilePath({
canvasId: 'qrCanvas',
success: (res) => {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
wx.showToast({
title: '保存しました',
icon: 'success'
})
}
})
}
})
}
}
}
4. パフォーマンス最適化のポイント
- Canvas APIの直接使用により、DOM操作を削減
- QRコードのエラー訂正レベルを適切に設定(H/M/L/Q)
- 大規模なQRコード生成時はWeb Workerの使用を検討
- 生成したQRコード画像のキャッシュ機構を実装
実装においては、Canvas APIを直接使用するアプローチがパフォーマンス面で有利です。特に大量のQRコードを生成する場合や、リアルタイムでの更新が必要なケースでは効果的です。