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

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

相关推荐
JIngJaneIL7 小时前
基于Java + vue干洗店预约洗衣系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
搬山境KL攻城狮7 小时前
记-SPA单页面应用Chrome自动翻译导致中文错别字问题
前端·chrome
HIT_Weston7 小时前
61、【Ubuntu】【Gitlab】拉出内网 Web 服务:Gitlab 配置审视(五)
前端·ubuntu·gitlab
剑小麟7 小时前
vue2项目中安装vant报错的解决办法
vue.js·java-ee·vue
旺仔Sec7 小时前
2026年度河北省职业院校技能竞赛“Web技术”(高职组)赛项竞赛任务
运维·服务器·前端
用户4099322502127 小时前
Vue的Class绑定对象语法如何让动态类名切换变得直观高效?
前端·ai编程·trae
瘦的可以下饭了7 小时前
Day01-API
javascript
GIS之路8 小时前
GIS 数据转换:GDAL 实现将 CSV 转换为 Shp 数据(一)
前端
Nan_Shu_6148 小时前
学习:Vue (2)
javascript·vue.js·学习