Vue3 二维码生成器实现方案(本项目实战拆解)
本文基于本项目的「二维码生成器」工具,拆解一套在 Vue3 / Nuxt3 项目中实现可视化二维码生成器的完整方案,重点放在页面结构与功能 JavaScript 的协作方式上,代码均来源于实际线上工具。
在线工具网址:https://see-tool.com/qr-code-generator
工具截图:
一、整体架构设计
本工具采用「Vue 负责结构和状态容器、独立 JS 负责绘制与交互」的分层思路:
- Vue 页面组件:
pages/qr-code-generator.vue,负责:- 渲染所有表单控件(文本/URL/邮箱/电话/SMS/WiFi/vCard 等类型)
- 输出带有
data-*标记的 DOM 结构,作为 JS 工具层的「挂载点」 - 提供预览区域、下载按钮区域,以及文档说明和相关工具组件
- 功能脚本一:
public/js/qr-code-generator-ui.js,只处理:- 类型卡片的选中态切换(
.qr-type-card) - 不同类型表单块的显隐(
.qr-input-form) - 监听
render-event/ 路由变化重新绑定
- 类型卡片的选中态切换(
- 功能脚本二:
public/js/qr-code-generator-tool.js,负责核心能力:- 动态加载第三方
qrcode-generator库 - 解析不同类型表单内容并组装成最终字符串
- 根据参数(尺寸、容错等级、前景色/背景色、点样式、定位角样式、中心 Logo)绘制二维码
- 生成 Canvas,并支持导出 PNG / SVG
- 监听输入变化,已生成过一次后自动「实时预览重绘」
- 动态加载第三方
这种拆分的好处是:Vue 只关心「结构和语义」,复杂的 Canvas 绘制逻辑完全放在独立 JS 中,通过data-qr-generator这一属性完成耦合。
二、Vue 页面:用>三、UI 交互层:类型切换与表单显隐
public/js/qr-code-generator-ui.js是一个自执行函数,仅做 UI 层交互:
setActiveType(root, type):- 遍历所有
.qr-type-card,给当前类型加上active,其它移除 - 遍历
.qr-input-form,data-form === type的移除hidden,其余加上hidden
- 遍历所有
bind(root):- 找到默认
active的卡片,或回退到text类型 - 给每张卡片绑定
click事件,切换类型
- 找到默认
init():- 通过
document.querySelectorAll('[data-qr-generator]')找到所有实例并绑定 - 监听
DOMContentLoaded、render-event、hashchange、popstate,确保在 SSR 渲染完成、前端路由切换后都能重新扫描并绑定
- 通过
这一层刻意不碰任何二维码算法或 Canvas,只负责「用户点哪里,看到哪个表单」。
四、核心工具层:从表单到二维码的完整流水线
public/js/qr-code-generator-tool.js是真正的核心,实现步骤可以拆成几块。
1. 环境准备与工具函数
loadScriptOnce(src):按 src 检查是否已经插入<script>,避免重复加载;如果脚本标签存在但库尚未就绪,则监听load/error。hexToRgba/isValidHexColor:处理颜色合法性与转换。normalizeUrl(url):为 URL 类型做 https 默认补全。getActiveType / getActiveDotStyle / getActiveEyeStyle:根据当前 DOM 选中态读出类型和样式。
2. 按类型组装内容字符串
getContentByType(root, type)是「业务协议」的核心,它根据类型从表单里取值,并组装不同的目标格式:
text:直接取纯文本;url:调用normalizeUrl,自动补https://;email:拼接为mailto:协议,支持 subject / body 参数;phone:tel:协议;sms:sms:号码?body=内容;wifi:按照标准 WiFi QR 协议拼出WIFI:T:WPA;S:SSID;P:PASSWORD;H:true;;;vcard:生成一个BEGIN:VCARD/END:VCARD的多行 vCard 文本,包含姓名、公司、电话、邮箱、网址等。
如果必填字段为空(如邮箱地址、WiFi SSID、vCard 姓名),函数会返回空字符串,后续生成逻辑会弹出「请填写必填字段」的通知而不是生成无效二维码。
3. Canvas 绘制:点阵 + 定位角 + Logo
生成二维码视觉效果的关键在buildQrCanvas:
- 通过第三方库
window.qrcode(0, errorLevel)生成二维码矩阵:addData(text)/make()得到qr.getModuleCount()和qr.isDark(row, col)。
- 创建 Canvas 并先用背景色铺底。
- 遍历矩阵:
- 通过
isInEyeFrame(row, col, moduleCount)判断当前 cell 是否属于三个位于角落的定位图形; - 非定位区域调用
drawDots(ctx, x, y, cellSize, dotStyle):square:纯方块;rounded:通过roundRect或手写 path 实现圆角矩形;dots:圆点样式。
- 通过
- 三个定位角通过
drawEyeFrame单独绘制,支持多种样式:- 通过 path / arc / 手写 roundRect 实现 diamond / leaf / dot / circle / extra-rounded / classy 等;
- 外框、内框、中心块用深浅色组合绘制。
- 如果用户上传了中心 Logo:
- 把图片读成 DataURL,
img.onload后在中心开一块浅底矩形,再绘制缩放后的 logo 图片。
- 把图片读成 DataURL,
最终返回一个 Promise,resolve 出完整绘制好的 Canvas。
4. 从 Canvas 导出 SVG
canvasToSvg(canvas, colorLight, colorDark)负责把渲染结果转成矢量 SVG:
- 读取整张 Canvas 的
imageData; - 先输出一个背景
rect,用浅色填充; - 再按像素遍历,凡是足够「黑」的像素,就在 path 中追加
M x,y h1 v1 h-1 z这种 1×1 小方块; - 最终得到一个单 path 的 SVG,方便下载和后续放大使用。
5. 绑定页面:生成、实时重绘与下载
bindQrTool(root)负责把整条流水线串起来:
- 先通过各种
data-field、.pattern-selector、.eye-frame-selector把 DOM 节点引用缓存下来; - 同步尺寸滑块与文字显示、同步颜色选择器与文本输入;
- 处理 Logo 上传、清除,以及文件名展示。
核心的生成逻辑在generate():
ensureLib():按需加载qrcode-generator.min.js,只加载一次;- 读取当前类型与内容,若为空则调用
window.showNotification提示并终止; - 读取尺寸、容错等级、颜色、点样式、定位角样式、Logo 等参数;
- 清空预览 DOM,调用
buildQrCanvas得到 Canvas; - 把 Canvas append 到预览容器中,并展示下载按钮;
- 记录
lastCanvas与hasGeneratedOnce标记,后续即可支持「改参数自动重绘」。
为了让体验更「所见即所得」,还提供了防抖重绘:
debounce(generate, 250)得到debouncedRegenerate;- 在尺寸滑块、容错等级选择、颜色输入、点样式/定位角样式、Logo 上传与清除、以及所有
.qr-input-form中的input/textarea/select/checkbox变动时触发; - 如果尚未生成过一次,则不会重绘,避免无意义计算。
最后是下载逻辑:
- PNG 下载:
lastCanvas.toDataURL('image/png')赋给临时<a>的href,设置download='qrcode.png'后触发点击; - SVG 下载:先用
canvasToSvg拿到字符串,再用 Blob +URL.createObjectURL生成临时链接,设置download='qrcode.svg'后点击并释放 URL。
五、在 Vue 项目中复用这套方案的要点
总结这套实现的关键点,其实就是一句话:Vue 负责结构和「可操作的标记」,具体的绘制逻辑用独立 JS 模块承载,通过>
VibeVoice-Realtime-0.5B实战:自定义音色微调数据准备指南
VibeVoice-Realtime-0.5B实战:自定义音色微调数据准备指南 想不想让AI用你自己的声音说话?或者为你心爱的角色、品牌打造一个独一无二的专属语音?VibeVoice-Realtime-0.5B这个强大的实时语音合成模型,除了自带的25种音色…
漫画脸提示词生成器:Vue前端集成Qwen3-32B模型实战
漫画脸提示词生成器:Vue前端集成Qwen3-32B模型实战 1. 为什么需要一个漫画脸提示词生成器 你有没有遇到过这样的情况:想用AI画一幅二次元角色,却卡在第一步——不知道该怎么描述?输入“一个女孩”,生成的可能是写实风…
SeqGPT-560M二维码生成与识别:iuiui技术集成方案
SeqGPT-560M二维码生成与识别:iuiui技术集成方案 1. 从文字到二维码的智能桥梁 最近在做一批需要快速生成和验证二维码的项目,发现传统方案总在几个地方卡住:要么生成的二维码样式单一,要么识别精度不够稳定,更麻烦的…
实时生成2K画面却零卡顿,Seedance2.0到底动了哪3根底层管线?,从CUDA Graph到Vulkan Compute全链路复盘
第一章:Seedance2.0实现实时2K画面零卡顿的系统级突破Seedance2.0并非简单升级编解码器或提升GPU频率,而是重构了从视频采集、内存调度、GPU管线到显示合成的全栈路径。其核心突破在于引入“帧生命周期感知内存池(FLAMP)”机制&am…
OFA图像英文描述模型部署:轻量distilled版在边缘设备(Jetson Orin)可行性初探
OFA图像英文描述模型部署:轻量distilled版在边缘设备(Jetson Orin)可行性初探 1. 项目概述 今天我们来聊聊一个特别实用的AI应用——如何在Jetson Orin这样的边缘设备上部署轻量级的图像描述模型。具体来说,我们要探索的是iic/o…
40%精度提升:Codeforces竞赛者的实时rating预测工具如何重构竞赛体验
40%精度提升:Codeforces竞赛者的实时rating预测工具如何重构竞赛体验 【免费下载链接】carrot A browser extension for Codeforces rating prediction 项目地址: https://gitcode.com/gh_mirrors/carrot1/carrot 在算法竞赛的世界里,每一次提交都…