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

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

相关推荐
user86158185781542 分钟前
Element UI 表格 show-overflow-tooltip 长文本导致闪烁的根本原因与解法
前端
不会写前端的小丁5 分钟前
前端首屏渲染性能优化小技巧
前端
还不秃顶的计科生5 分钟前
defaultdict讲解
开发语言·javascript·ecmascript
晴虹6 分钟前
lecen:一个更好的开源可视化系统搭建项目--组件和功能按钮的权限控制--全低代码|所见即所得|利用可视化设计器构建你的应用系统-做一
前端·后端·低代码
爱分享的鱼鱼9 分钟前
Pinia 深度解析:现代Vue应用状态管理最佳实践
前端·后端
花归去10 分钟前
echarts 柱状图包含右侧进度
开发语言·前端·javascript
沐浴露z21 分钟前
学习通“只能录入不能粘贴” 解决方案与原理分析
javascript
多看书少吃饭26 分钟前
Vite开发环境按需编译是怎么实现的
前端
ybb_ymm34 分钟前
@Async修饰不生效
java·前端·数据库
Sapphire~37 分钟前
Vue3-03 熟悉src文件夹及Vue文件格式
前端·javascript·vue.js