基于qrcode前端实现链接转二维码的生成与下载

一、应用场景

我们经常需要将链接url生成二维码并下载,有时还需在图片上添加说明文字,如下图所示:

类似实现的在线工具有很多,在前端项目开发过程中要实现,以下说明一种方法,可直接copy使用:

二、实现方式:

1、安装QRCode

bash 复制代码
npm install qrcode --save

bash 复制代码
yarn add  qrcode --save

2、封装一个根据url生成并下载的工具函数

我这里是vue+ts项目,封装了个工具函数generateQRCode, 可直接copy至项目中

javascript 复制代码
import QRCode from 'qrcode'

/**
 * 二维码图片配置选项
 */
export interface QRCodeImageOptions {
  url: string  // 要生成二维码的URL地址
  fileName?: string // 下载的文件名,默认为'qrcode.png'
  canvasWidth?: number // 画布宽度,当有文字内容时使用,默认为400
  canvasHeight?: number // 画布高度,当有文字内容时使用,默认为400
  qrSize?: number // 二维码尺寸,默认为380
  textContent?: string // 文字内容,如果传入则显示文字,不传入则生成纯二维码
  // 文字配置选项
  textConfig?: {
    x?: number // 文字起始X坐标
    y?: number // 文字起始Y坐标
    lineHeight?: number // 文字行高,默认为24
    maxWidth?: number // 文字最大宽度,超过会自动换行
    fontSize?: string // 文字字体样式,默认为'48px Microsoft YaHei'
    color?: string // 文字颜色,默认为'black'
  }
}

/**
 * 文字换行处理函数
 */
function wrapText( ctx: CanvasRenderingContext2D, text: string, x: number, y: number, maxWidth: number, lineHeight: number): void {
  const chars = text.split('')
  let line = ''
  let testLine = ''

  // 设置精确文本测量基线
  ctx.textBaseline = 'alphabetic'
  let xCoord = x
  
  for (let i = 0; i < chars.length; i++) {
    testLine = line + chars[i]
    const metrics = ctx.measureText(testLine)

    if (metrics.width > maxWidth && i > 0) {
      ctx.fillText(line, x, y)
      line = chars[i]
      y += lineHeight
    } else {
      line = testLine
      // 居中显示文字
      xCoord = (maxWidth - metrics.width) / 2 + 40
    }
  }
  ctx.fillText(line, xCoord, y)
}

/**
 * 生成并下载二维码图片
 * @param options 二维码图片配置选项
 * @returns Promise<boolean> 是否成功生成并下载
 */
export async function generateQRCode(options: QRCodeImageOptions): Promise<boolean> {
  const {
    url,
    fileName = 'qrcode.png',
    canvasWidth = 660,
    canvasHeight = 700,
    qrSize = 600,
    textContent = '',
    textConfig = {
      x: 40,
      y: 630,
      lineHeight: 48,
      maxWidth: 580, // canvasWidth - textConfig.x
      fontSize: '48px Microsoft YaHei',
      color: 'black'
    }
  } = options

  try {
    // 创建隐藏的canvas元素
    const canvas = document.createElement('canvas')
    
    // 如果有文字内容,使用更大的画布;否则使用二维码尺寸
    if (textContent) {
      canvas.width = canvasWidth
      canvas.height = canvasHeight
    } else {
      canvas.width = qrSize
      canvas.height = qrSize
    }
    
    const ctx = canvas.getContext('2d')
    
    if (!ctx) {
      throw new Error('无法获取canvas上下文')
    }

    // 绘制背景
    ctx.fillStyle = 'white'
    ctx.fillRect(0, 0, canvas.width, canvas.height)

    // 生成二维码
    const qrDataURL = await QRCode.toDataURL(url, { width: qrSize })
    await new Promise<void>((resolve) => {
      const img = new Image()
      img.onload = () => {
        // 如果有文字内容,居中绘制二维码;否则直接绘制
        if (textContent) {
          const margin = (canvasWidth - qrSize) / 2
          ctx.drawImage(img, margin, 20, qrSize, qrSize)
        } else {
          ctx.drawImage(img, 0, 0, qrSize, qrSize)
        }
        resolve()
      }
      img.src = qrDataURL
    })

    // 如果有文字内容,绘制文字
    if (textContent) {
      // 设置文字样式
      ctx.fillStyle = textConfig.color || 'black'
      ctx.font = textConfig.fontSize || '48px "Microsoft YaHei", sans-serif'
      ctx.textBaseline = 'alphabetic'
      
      // 确保字体设置生效
      await new Promise(resolve => setTimeout(resolve, 10))
      
      wrapText(
        ctx,
        textContent,
        textConfig.x || 40,
        textConfig.y || 630,
        textConfig.maxWidth || 580,
        textConfig.lineHeight || 48
      )
    }

    // 触发下载
    const link = document.createElement('a')
    link.download = fileName
    link.href = canvas.toDataURL('image/png')
    link.click()

    // 清理canvas元素
    canvas.remove()
    
    return true
  } catch (error) {
    console.error('生成二维码失败:', error)
    return false
  }
}

3、在实际业务中调用

javascript 复制代码
generateQRCode({
		textContent: '你的描述文字',
		fileName: '自定义文件名' || new Date().getTime() + ".png",
		url: '生成二维码的链接url',
		textConfig: { // 根据你需要配置,具体看函数定义说明
			fontSize: 32px Microsoft YaHei',
			lineHeight: 40,
		}
	})

end

希望记录的问题能帮助到你!

相关推荐
奔跑的web.20 小时前
TypeScript 装饰器入门核心用法
前端·javascript·vue.js·typescript
阿蒙Amon21 小时前
TypeScript学习-第1章:入门
javascript·学习·typescript
winfredzhang21 小时前
实战复盘:如何用 HTML+JS+AI 打造一款“影迹”智能影视管理系统
javascript·html·json·加载·搜索·保存·电影接口
集成显卡21 小时前
Lucide Icons:一套现代、轻量且可定制的 SVG 图标库
前端·ui·图标库·lucide
pas13621 小时前
37-mini-vue 解析插值
前端·javascript·vue.js
十里-1 天前
vue.js 2前端开发的项目通过electron打包成exe
前端·vue.js·electron
雨季6661 天前
构建 OpenHarmony 简易文字行数统计器:用字符串分割实现纯文本结构感知
开发语言·前端·javascript·flutter·ui·dart
雨季6661 天前
Flutter 三端应用实战:OpenHarmony 简易倒序文本查看器开发指南
开发语言·javascript·flutter·ui
小北方城市网1 天前
Redis 分布式锁高可用实现:从原理到生产级落地
java·前端·javascript·spring boot·redis·分布式·wpf
console.log('npc')1 天前
vue2 使用高德接口查询天气
前端·vue.js