基于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

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

相关推荐
喜欢踢足球的老罗7 分钟前
一张跨域图的“四次换乘“:blob URL 与 Chrome 扩展架构里的工程艺术
前端·chrome·架构
程序员黑豆8 分钟前
AI全栈开发 - Java:基本数据类型 vs 引用数据类型的内存存储
java·前端·ai编程
FserSuN9 分钟前
Chrome CORS / PNA / LNA 问题排查与解决方案
前端·chrome
小小小小宇17 分钟前
Claude Code 自动运行方法大全
前端
道友可好19 分钟前
AI 测试全绿,代码却是错的
前端·人工智能·后端
国科安芯38 分钟前
商业航天通信载荷数字处理单元供电架构研究——基于ASP7A84AS的高精度低压差线性稳压器技术分析
前端·单片机·嵌入式硬件·fpga开发·架构·安全性测试
TangentDomain1 小时前
AI 写代码时代,游戏 UI 架构为什么停在 MVP?
前端·游戏·架构
夕夕木各1 小时前
探究 TypeScript 类型体操里的 Equal 和 IsAny
typescript·源码阅读
英勇无比的消炎药1 小时前
前端提效神器全新AI组件库TinyRobot改写日常开发模式
前端·vue.js