Vue.jsによるQRコードの動的生成とダウンロード機能の実装

フロントエンドでユーザーの個人名刺用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コードを生成する場合や、リアルタイムでの更新が必要なケースでは効果的です。

タグ: vue.js QRコード Canvas javascript フロントエンド

9月8日 11:19 投稿