Files
hunli/vite-tailwindcss/src/composables/renderFestiveQr.js
2026-08-03 10:18:23 +08:00

84 lines
2.1 KiB
JavaScript

import QRCode from 'qrcode'
function loadImage(src) {
return new Promise((resolve, reject) => {
const img = new Image()
img.crossOrigin = 'anonymous'
img.onload = () => resolve(img)
img.onerror = reject
img.src = src
})
}
/** 在 canvas 上绘制喜庆二维码,中心为配置图或圆圈大囍 */
export async function renderFestiveQr(canvas, url, options = {}) {
const size = options.size || 148
const centerImg = String(options.centerImg || '').trim()
await QRCode.toCanvas(canvas, url, {
width: size,
margin: 1,
color: {
dark: '#8b0000',
light: '#fff8e7',
},
errorCorrectionLevel: 'H',
})
const ctx = canvas.getContext('2d')
if (!ctx) return
const w = canvas.width
const h = canvas.height
const cx = w / 2
const cy = h / 2
const diameter = Math.round(Math.min(w, h) * 0.3)
const r = diameter / 2
const ring = Math.max(2, Math.round(w * 0.012))
ctx.save()
// 外圈底
ctx.beginPath()
ctx.arc(cx, cy, r + ring, 0, Math.PI * 2)
ctx.fillStyle = '#fff8e7'
ctx.fill()
ctx.strokeStyle = '#c41e3a'
ctx.lineWidth = ring
ctx.stroke()
let drewImg = false
if (centerImg) {
try {
const img = await loadImage(centerImg)
ctx.beginPath()
ctx.arc(cx, cy, r - 0.5, 0, Math.PI * 2)
ctx.clip()
const side = (r - 0.5) * 2
const scale = Math.max(side / img.width, side / img.height)
const dw = img.width * scale
const dh = img.height * scale
ctx.drawImage(img, cx - dw / 2, cy - dh / 2, dw, dh)
drewImg = true
} catch {
drewImg = false
}
}
if (!drewImg) {
ctx.beginPath()
ctx.arc(cx, cy, r - 0.5, 0, Math.PI * 2)
ctx.fillStyle = 'rgba(255, 248, 231, 0.96)'
ctx.fill()
ctx.strokeStyle = '#c41e3a'
ctx.lineWidth = Math.max(1.5, ring * 0.7)
ctx.stroke()
ctx.fillStyle = '#c41e3a'
ctx.font = `700 ${Math.round(r * 1.45)}px "Noto Serif SC","Source Han Serif SC","Songti SC",serif`
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.fillText('囍', cx, cy + r * 0.04)
}
ctx.restore()
}