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

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

二维码库的选择

前端生成二维码的库挺多的,我们用的是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

本地多模型切换利器——Llama-Swap全攻略

本地多模型切换利器——Llama-Swap全攻略

运行多个大语言模型(LLM)非常有用: 无论是用于比较模型输出、设置备用方案(当一个模型失败时自动切换)、还是实现行为定制(例如一个模型专注写代码,另一个模型专注技术写作),实践中我们经常以这种方式使用 LLM。 一些应用(如 poe.com)已经提供了多模型运行的平台。但如果你希望完全在本地运行、多省 API 成本,并保证数据隐私,情况就会复杂许多。 问题在于:本地设置通常意味着要处理多个端口、运行不同进程,并且手动切换,不够理想。 这正是 Llama-Swap 要解决的痛点。它是一个超轻量的开源代理服务(仅需一个二进制文件),能够让你轻松在多个本地 LLM 之间切换。简单来说,它会在本地监听 OpenAI 风格的 API 请求,并根据请求的模型名称,自动启动或停止对应的模型服务。客户端无需感知底层切换,使用体验完全透明。 📌 Llama-Swap 工作原理 概念上,Llama-Swap 就像一个智能路由器,

虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理

虚幻版Pico大空间VR入门教程 05 —— 原点坐标和项目优化技巧整理

大空间头显坐标朝向 一体机p4ue设备开启大空间定位识别,并框选障碍物范围,暂时不用Marker贴图精细定位。 大空间一体机范围设置文档:https://business.picoxr.com/cn/doc/Enterprise-LBE-Bestcase 非企业版设备(具体设备型号查看02章节),如果没有内置大空间功能,则需要使用第三方定位,例如RTK,ubw。 通过UE5默认的VR项目模板,修改SetTrackingOrigin记录:(括号内为UE5.3及以前旧版本枚举选项) OpenXR有线串联模式下, Stage、View(eye、EyeLevel)、Local floor(StandFloor、FloorLevel)、Local(SitFloor)、customOpenXR模式下,大空间标记障碍物和VR场景障碍物匹配, Reset Orientation and Position 则以当前玩家的位置和头显正朝向作为场景默认初始点; (如果玩家朝向东南时触发重置头显,则在VR游戏内,现实世界的东南朝向为VR游戏内的正北, 在大空间模式下需要特别注意重置头显操作,否

盘古200Pro+开发板高速通信指南:6.6Gbps光纤+PCIe x4性能实测

盘古200Pro+开发板高速通信实战:从6.6Gbps理论到稳定传输的工程化路径 对于从事通信系统、数据中心互联或高速数据采集的工程师而言,一块标称性能强劲的FPGA开发板到手后,最令人兴奋也最具挑战的环节,莫过于将手册上的理论速率转化为稳定、可靠的实测性能。紫光同创的盘古200Pro+开发板(基于PG2L200H FPGA,型号MES2L676-200HP)以其高达6.6Gbps的HSST收发器速率和PCIe x4接口,吸引了众多追求高性能国产化方案开发者的目光。然而,在真实的工程环境中,如何驾驭这些高速接口,确保数据传输的完整性与低延迟,远非接上光纤或插入PCIe插槽那么简单。本文将从一个实践者的角度,深入探讨如何在这块开发板上实现光纤与PCIe链路的高性能通信,分享从时钟优化、眼图分析到系统级调优的全流程实战经验。 1. 硬件平台深度解析与高速链路设计要点 盘古200Pro+开发板采用核心板加扩展底板的架构,其高速通信能力的基石在于紫光同创Logos2系列PG2L200H FPGA内嵌的HSST(High-Speed Serial Transceiver)模块。官方宣称每

无人机航拍图像拼接:自动识别重叠区域完成合成

无人机航拍图像拼接:自动识别重叠区域完成合成 引言:从航拍痛点出发,为何需要智能图像拼接? 在农业监测、城市规划、灾害评估等场景中,无人机航拍已成为获取高分辨率地表信息的核心手段。然而,单张航拍图像视野有限,必须通过多张图像拼接才能生成完整的全景图或地图。传统拼接方法依赖SIFT、ORB等特征匹配算法,在光照变化大、纹理重复(如农田、屋顶)的场景下容易失效,导致错位、断裂甚至拼接失败。 更关键的是,传统流程需手动指定图像对的重叠区域,效率低下且难以自动化。随着AI视觉技术的发展,基于深度学习的万物识别模型为解决这一问题提供了新思路——让模型“看懂”图像内容,自动判断哪些区域是重叠的,从而实现端到端的智能拼接。 本文将结合阿里开源的万物识别-中文-通用领域模型,构建一套完整的无人机航拍图像自动拼接方案,重点讲解如何利用该模型语义理解能力精准识别图像重叠区域,并完成高质量合成。 技术选型:为什么选择“万物识别-中文-通用领域”模型? 模型背景与核心优势 “万物识别-中文-通用领域”是阿里巴巴通义实验室推出的一款面向中文场景的通用图像识别模型。其核心目标是实现对日常物体、自