二维码生成器:从前端到打印的全流程

二维码生成器:从前端到打印的全流程

二维码库的选择

前端生成二维码的库挺多的,我们用的是qrcode这个npm包,star数比较多,维护也比较活跃。

npm install qrcode 

项目里的版本是1.5.4。

基本用法

二维码生成非常简单:

import QRCode from "qrcode"; QRCode.toDataURL("https://example.com").then(url => { console.log(url); // base64格式的图片数据 }); 

在我们的项目里,二维码生成是在支付场景下用的。用户选择充值套餐后,后端返回一个支付链接,前端把这个链接转成二维码显示:

<template> <el-dialog title="支付二维码" :visible.sync="openWeixinCode"> <div>¥ {{ rechargeList[activeRecharge].price }}</div> <el-image :src="orderInfo.url" fit="contain"></el-image> <div>请使用微信扫一扫,扫描二维码完成支付</div> </el-dialog> </template> <script> import QRCode from "qrcode"; export default { methods: { handlePay() { buyRechargePower(this.rechargeList[this.activeRecharge].id).then(res => { QRCode.toDataURL(res.data.url).then(url => { this.orderInfo.url = url; this.openWeixinCode = true; // 轮询支付状态 this.timer = setInterval(() => { this.getEnOrderStatus(); }, 2000); }); }); } } } </script> 

第一个坑:二维码太复杂扫不出来

一开始生成的二维码特别复杂,感觉密密麻麻全是点,手机扫码很难识别。

后来查了一下文档,发现可以通过errorCorrectionLevel参数调整容错率:

QRCode.toDataURL(url, { errorCorrectionLevel: 'L' // L、M、Q、H,容错率从低到高 }).then(url => { // ... }); 

容错率越高,二维码越复杂但越抗污损。对于我们的场景(线上扫码显示在手机屏幕上),选L级别就够了,二维码会简洁很多,扫码速度快很多。

第二个坑:二维码尺寸和清晰度

默认生成的二维码分辨率不太高,在高清屏上有点模糊。可以设置width参数:

QRCode.toDataURL(url, { width: 300, // 宽度,单位是像素 margin: 2, // 边距,留白区域 color: { dark: '#000000', // 二维码颜色 light: '#FFFFFF' // 背景颜色 } }).then(url => { // ... }); 

这里有个坑:width设置太大,base64字符串会非常长,可能会超出浏览器localStorage的长度限制。一般300-400就够用了。

打印适配

名片是要打印的,所以二维码在打印时的效果很重要。

我们用的是html2canvas把整个名片转成图片,然后打印。这里有几个要注意的点:

1. 设置合适的DPI

打印用的图片分辨率要比屏幕显示的高。我们是在生成名片的时候,整体放大2倍:

import html2canvas from 'html2canvas'; html2canvas(element, { scale: 2, // 2倍分辨率 useCORS: true, // 支持跨域图片 backgroundColor: '#ffffff' }).then(canvas => { // canvas转图片... }); 

2. 二维码大小要合适

名片尺寸通常是90mm × 54mm,二维码占的比例不能太大,否则扫的时候距离要拉得很远;也不能太小,否则打印出来不清楚。

我们测试下来,二维码宽度在名片总宽度的30%-40%比较合适。比如名片宽度360px,二维码大概120-140px。

3. 考虑留白

二维码周围要留点白边,不然打印出来边缘被裁了可能就扫不出来了:

QRCode.toDataURL(url, { margin: 2, // 至少留2个模块的边距 // ... }); 

下载二维码图片

有时候用户想单独下载二维码图片,可以直接用base64转blob下载:

function downloadQRCode(text, filename) { QRCode.toDataURL(text).then(url => { const link = document.createElement('a'); link.href = url; link.download = filename || 'qrcode.png'; link.click(); }); } 

或者配合file-saver库:

import { saveAs } from 'file-saver'; QRCode.toBlob(text).then(blob => { saveAs(blob, 'qrcode.png'); }); 

实时刷新支付状态

二维码生成后,要轮询后端接口看用户有没有支付成功:

handlePay() { buyRechargePower(this.rechargeList[this.activeRecharge].id).then(res => { this.orderInfo = res.data; QRCode.toDataURL(res.data.url).then(url => { this.orderInfo.url = url; this.openWeixinCode = true; // 每2秒查询一次支付状态 this.timer = setInterval(() => { this.getEnOrderStatus(); }, 2000); }); }); }, getEnOrderStatus() { getEnOrderStatus(this.orderInfo.orderId).then(res => { if (res.data.status == 1) { // 支付成功,清除定时器 if (this.timer) { clearInterval(this.timer); } this.$message.success("支付成功"); this.openWeixinCode = false; this.$emit("RechargeSuccess"); } }); } 

这里要注意的是,组件销毁的时候一定要清除定时器:

beforeDestroy() { if (this.timer) { clearInterval(this.timer); } } 

一些小技巧

1. 二维码加Logo

可以在生成的二维码中间加个小Logo,不过要注意:

  • Logo不能太大,一般占二维码面积的15%-20%
  • Logo颜色要和二维码有对比度
  • 最好用白色边框把Logo围起来

这个qrcode库本身不支持加Logo,得自己处理canvas。我们项目暂时没这个需求,就没做。

2. 批量生成

如果需要批量生成二维码,记得用Promise.all并行处理:

const urls = ['url1', 'url2', 'url3']; Promise.all(urls.map(url => QRCode.toDataURL(url))).then(results => { console.log(results); // 三张二维码的base64 }); 

3. 打印预览

打印前最好给用户一个预览功能,确认效果没问题再打印。浏览器有原生打印API:

window.print(); 

配合CSS的@media print可以控制打印时的样式:

@media print { .no-print { display: none; } .qrcode-container { page-break-inside: avoid; } } 

总结

二维码功能虽然看起来简单,但真要做好细节还挺多的:

  1. 容错率要选对:根据使用场景调整,别一味追求高容错
  2. 尺寸要合适:屏幕显示和打印的尺寸要求不一样,要分别优化
  3. 打印效果要测试:别光看屏幕上的效果,打印出来看看实际效果
  4. 定时器要清理:轮询类的代码一定要在组件销毁时清除定时器

Read more

速通前端篇 —— HTML

速通前端篇 —— HTML

找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程程(ಥ_ಥ)-ZEEKLOG博客 所属专栏:速通前端 目录 HTML的介绍 如何创建HTML文件 HTML 文件基本结构 HTML常用标签 title标签   标题标签 h1-h6  段落标签 p 换行标签 br 图片标签 img  超链接 a 表格标签 table 表单标签 input 标签 form 标签  select 标签 textarea 标签  无语义标签 div&span 列表标签  综合练习:用户登录  由于我们Java是属于后端开发的,因此对于前端部分,我们只需要简单了解,达到认识与编写基本的代码即可。  HTML的介绍 HTML(Hyper

ofa_image-caption代码实例:扩展支持WebP格式与EXIF元数据保留功能

ofa_image-caption代码实例:扩展支持WebP格式与EXIF元数据保留功能 1. 引言 你有没有遇到过这种情况?从手机或相机里导出一堆照片,想快速整理归档,却要一张张手动写描述,费时又费力。或者,在做内容创作时,需要为大量图片配上精准的英文说明,人工处理效率极低。 今天要介绍的这个工具,就是来解决这个痛点的。它叫 ofa_image-caption,是一个纯本地运行的图像描述生成工具。简单来说,你给它一张图,它就能用英文告诉你这张图里有什么。 这个工具的核心是基于一个叫 OFA 的模型,这个模型在图像描述生成领域表现很不错。我们之前发布的版本已经能很好地处理 JPG、PNG 这些常见格式了。但最近,越来越多的用户开始使用 WebP 这种更高效的图片格式,同时,很多摄影师和内容创作者也希望生成的描述能保留图片拍摄时的原始信息(比如拍摄时间、相机型号)。 所以,我们对这个工具进行了一次重要的升级。这篇文章,我就带你手把手看看,我们是如何在原有代码基础上,扩展了对 WebP 格式的支持,并实现了 EXIF 元数据的保留功能。

前端检查内存泄露

前言 前端应用的内存泄露,指不再使用内存未被释放,导致页面占用内存持续增长,轻则引发页面卡顿,加载缓慢,重则导致浏览器崩溃, 尤其在单页应用SPA中,路由切换频繁但内存不回收,问题会被无限放大,比如用户长时间使用某后台管理系统,可能出现操作响应式延迟,甚至需要强制刷新才能恢复,这很可能是内存泄露在"作祟" 一. 前端常见的内存泄露场景 1. 意外的全局变量:未声明的变量(如a = 10而非let a = 10)会挂载到window上,页面不刷新就不会释放; 2. 闭包滥用:闭包会保留对外部作用域的引用,若长期持有 DOM 或大型对象,会导致内存无法回收(如未清理的事件监听回调) ; 3. 未清理的 DOM 引用:删除 DOM 节点后,仍保留其引用(如let el =       document.getElementById('test&

VibeBlog-AI 时代个人博客Agent项目开源之路[9]: 基于ui-ux-pro-max 的前端重新设计

VibeBlog-AI 时代个人博客Agent项目开源之路[9]: 基于ui-ux-pro-max 的前端重新设计

开篇先介绍自己的开源项目vibe-blog, 一个基于多 Agent 架构的 "长文专业博客"的创作助手,支持深度调研、智能配图、Mermaid 图表、代码集成等写作能力,简化写作的重复劳动, 让写作更有趣. 我基于它已经创作了一个面向大模型应用开发者的微调(Fine-tuning)技术全栈教程Hello-LLM-FineTuning, 40 万字,100+章配图. 感兴趣的同学可以了解下,如果该项目对你有用, 欢迎 star🌟 & fork🍴 Vibe-Blog开源项目地址: https://github.com/datawhalechina/vibe-blog 先看前端重构效果: 怎么样😄, 还可以吧, 程序员的终端风格, 我超级喜欢! 缘起 Vibe-Blog 已经具备了一键生成长文博客的能力, 也支持异步创作的能力,即你可以直接将你想要创作博客的想法直接扔给 Vibe-Blog, 然后就可以去忙其他的了, 等过一段时间它自己生成好了, 你可以直接阅读他的成果, 也可以发布到一些博客平台上, 比如