前端实战:基于 Vue 与 QRCode 库实现动态二维码合成与下载功能

在现代 Web 应用开发中,二维码的应用越来越广泛,从电子票务到信息传递,它都扮演着重要角色。本文将分享如何在 Vue 项目中,结合QRCode库实现动态二维码的生成、与背景图合成以及图片下载功能,打造一个完整且实用的二维码展示模块,最终效果兼顾美观与交互性。

项目背景与需求分析

在我们的五龙山野生动物园票务项目中,需要为用户提供电子门票二维码展示页面。该页面不仅要展示清晰的门票二维码,还需结合动物园特色背景图,增强视觉体验。同时,为方便用户保存门票,需实现长按二维码保存合成图片的功能。这就涉及到二维码生成、图片合成以及用户交互逻辑的实现。

技术栈选择

本项目基于 Vue.js 框架进行开发,结合QRCode库用于二维码的生成,利用原生 JavaScript 的 Canvas API 完成图片合成与绘制,确保功能的高效与稳定。

代码实现详解

1. 样式设计(<style>部分)

* {margin: 0;padding: 0;
}.yard_box {width: 100%;height: 100vh;background-image: url('../assets/yard_back.png');background-size: cover;background-repeat: no-repeat;background-position: center;position: relative;
}.top_title {width: 100%;height: 50px;display: flex;
}.return_icon {width: 40px;height: 40px;
}.return_box {width: 40px;height: 40px;background-color: rgb(70, 58, 58);border-radius: 50%;opacity: 0.7;margin-left: 2%;margin-top: 2%;
}.qr-canvas {width: 200px;height: 200px;position: absolute;left: 51%;bottom: 180px;/* 调整二维码在页面中的位置 */transform: translateX(-50%);cursor: pointer;transition: transform 0.1s;
}.qr-canvas:active {transform: translateX(-50%) scale(0.98);
}.save-hint {position: absolute;bottom: 50px;left: 0;right: 0;text-align: center;color: white;font-size: 14px;text-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
}

 

样式部分首先通过*选择器重置默认样式,消除内外边距。yard_box类为整个页面设置了全屏背景图,营造沉浸式视觉效果。top_titlereturn_box实现了返回按钮的布局与样式,qr-canvas类定义了二维码画布的初始位置、大小及交互效果,通过active伪类实现按压时的缩放动效,提升交互体验。

2. 模板搭建(<template>部分)

<template><div class="yard_box"><div class="top_title" @click="$router.go(-1)"><div class="return_box"><img src="../assets/left.svg" alt="" class="return_icon"></div></div><!-- 显示二维码的画布 --><canvas ref="qrCanvas" @mousedown="startLongPress" @mouseup="cancelLongPress" @mouseleave="cancelLongPress"@touchstart="startLongPress" @touchend="cancelLongPress" @touchcancel="cancelLongPress"class="qr-canvas"></canvas></div>
</template>

模板中,外层yard_box包裹页面所有元素,top_title绑定点击事件实现返回上一页功能。<canvas>标签用于绘制二维码,同时绑定了鼠标和触摸事件,以兼容 PC 端与移动端的长按操作,为后续的保存功能提供交互基础。

3. 逻辑实现(<script>部分)

import QRCode from 'qrcode';
import yardBack from '@/assets/yard_back.png'; // 使用ES模块导入export default {data() {return {ticketId: '35',longPressTimer: null,longPressDuration: 1000, // 长按时间阈值(1秒)backgroundImage: new Image() // 用于合成图片的背景图};},methods: {async loadBackgroundImage() {return new Promise((resolve) => {this.backgroundImage.src = yardBack; // 使用导入的图片路径this.backgroundImage.onload = resolve;});},// 生成二维码generateQRCode() {const canvas = this.$refs.qrCanvas;QRCode.toCanvas(canvas, this.ticketId, {width: 200,margin: 2}, (error) => {if (error) console.error('二维码生成失败:', error);});},// 开始长按startLongPress() {this.longPressTimer = setTimeout(() => {this.saveMergedImage();}, this.longPressDuration);},// 取消长按cancelLongPress() {if (this.longPressTimer) {clearTimeout(this.longPressTimer);this.longPressTimer = null;}},// 创建并保存合并后的图片async saveMergedImage() {// 创建临时Canvas用于合成const mergedCanvas = document.createElement('canvas');const ctx = mergedCanvas.getContext('2d');// 设置Canvas尺寸与背景图一致mergedCanvas.width = this.backgroundImage.width;mergedCanvas.height = this.backgroundImage.height;// 1. 绘制背景图ctx.drawImage(this.backgroundImage, 0, 0, mergedCanvas.width, mergedCanvas.height);// 2. 计算白色区域的位置和大小(需要根据实际背景图调整)const whiteArea = {x: mergedCanvas.width * 0.2,    // 白色区域左侧位置(20%宽度处)y: mergedCanvas.height * 0.45,   // 白色区域顶部位置(60%高度处)width: mergedCanvas.width * 0.6, // 白色区域宽度(60%宽度)height: mergedCanvas.height * 0.3 // 白色区域高度(30%高度)};// 3. 计算二维码在白色区域中的合适大小和位置const qrCanvas = this.$refs.qrCanvas;const qrMaxSize = Math.min(whiteArea.width, whiteArea.height) * 0.8; // 二维码最大尺寸(白色区域的80%)const qrSize = Math.min(420, qrMaxSize); // 不超过200px// 计算居中位置const qrX = whiteArea.x + (whiteArea.width - qrSize) / 2;const qrY = whiteArea.y + (whiteArea.height - qrSize) / 2;// 绘制二维码ctx.drawImage(qrCanvas, qrX, qrY, qrSize, qrSize);// 4. 添加文字(调整到二维码上方)const textY = qrY - 30;ctx.font = 'bold 40px Arial';ctx.fillStyle = 'white';ctx.textAlign = 'center';ctx.fillText('', mergedCanvas.width / 2, textY);// 保存图片const link = document.createElement('a');link.download = `五龙山野生动物园门票-${this.ticketId}.png`;link.href = mergedCanvas.toDataURL('image/png');document.body.appendChild(link);link.click();document.body.removeChild(link);}},async mounted() {await this.loadBackgroundImage();await this.generateQRCode();}
};

JavaScript逻辑部分,首先导入QRCode库和背景图片资源。data中定义了门票 ID、长按计时器、长按阈值以及背景图片对象。

loadBackgroundImage方法通过 Promise 封装图片加载过程,确保图片加载完成后再进行后续操作。generateQRCode方法利用QRCode库将门票 ID 生成到指定的<canvas>上。

长按相关方法startLongPresscancelLongPress分别用于启动长按计时和取消计时,当长按达到设定阈值时,触发saveMergedImage方法。

saveMergedImage方法是核心逻辑,创建新的<canvas>用于合成图片,先绘制背景图,再计算并绘制白色区域、二维码,最后添加文字。通过创建<a>标签,利用toDataURL方法将合成后的图片转换为 URL,实现图片下载功能。mounted钩子函数确保页面加载时完成背景图加载和二维码生成。

效果展示与优化方向

最终实现的页面,背景图与二维码完美融合,用户长按二维码即可保存包含背景、二维码和文字信息的合成图片。后续优化可以考虑:

  1. 动态数据绑定:将ticketId改为动态获取,实现不同门票二维码的展示。
  2. 样式优化:根据实际背景图调整白色区域和文字样式,增强视觉美感。
  3. 错误处理:完善二维码生成和图片合成过程中的错误提示,提升用户体验。

通过以上步骤,我们成功在 Vue 项目中实现了一个功能完整、交互良好的二维码合成与下载模块。希望本文能为你的前端开发实践带来启发,在实际项目中灵活运用相关技术!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/901891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HAL详解

一、直通式HAL 这里使用一个案例来介绍直通式HAL&#xff0c;选择MTK的NFC HIDL 1.0为例&#xff0c;因为比较简单&#xff0c;代码量也比较小&#xff0c;其源码路径&#xff1a;vendor/hardware/interfaces/nfc/1.0/ 1、NFC HAL的定义 1&#xff09;NFC HAL数据类型 通常定…

Vue自定义指令-防抖节流

Vue2版本 // 防抖 // <el-button v-debounce"[reset,click,300]" ></el-button> // <el-button v-debounce"[reset]" ></el-button> Vue.directive(debounce, { inserted: function (el, binding) { let [fn, event "cl…

AI知识补全(十六):A2A - 谷歌开源的agent通信协议是什么?

名人说&#xff1a;一笑出门去&#xff0c;千里落花风。——辛弃疾《水调歌头我饮不须劝》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 上一篇&#xff1a;AI知识补全&#xff08;十五&#xff09;&#xff1a;AI可解…

【机器人创新创业应需明确产品定位与方向指南】

机器人领域的创新创业, 需要对公司和产品的定位和生态进行深入思考, 明确其定位与发展目标, 明确产品在是为G、为B还是为C进行服务。 本文引用地址&#xff1a;https://www.eepw.com.cn/article/202504/469401.htm 超前的、探索性的创新技术一般是面向G端, 而不是面向B端或者C…

网安加·百家讲坛 | 刘志诚:AI安全风险与未来展望

作者简介&#xff1a;刘志诚&#xff0c;乐信集团信息安全中心总监、OWASP广东区域负责人、网安加社区特聘专家。专注于企业数字化过程中网络空间安全风险治理&#xff0c;对大数据、人工智能、区块链等新技术在金融风险治理领域的应用&#xff0c;以及新技术带来的技术风险治理…

TOA与AOA联合定位的高精度算法,三维、4个基站的情况,MATLAB例程,附完整代码

本代码实现了三维空间内目标的高精度定位,结合到达角(AOA) 和到达时间(TOA) 两种测量方法,通过4个基站的协同观测,利用最小二乘法解算目标位置。代码支持噪声模拟、误差分析及三维可视化,适用于无人机导航、室内定位等场景。订阅专栏后可获得完整代码 文章目录 运行结果…

2025MathorcupC题 音频文件的高质量读写与去噪优化 保姆级教程讲解|模型讲解

2025Mathorcup数学建模挑战赛&#xff08;妈妈杯&#xff09;C题保姆级分析完整思路代码数据教学 C题&#xff1a;音频文件的高质量读写与去噪优化 随着数字媒体技术的迅速发展&#xff0c;音频处理成为信息时代的关键技术之一。在日常生活中&#xff0c;从录音设备捕捉的原始…

Deno Dep:颠覆传统的模块化未来

一、重新定义依赖管理&#xff1a;Deno Dep 的革新哲学 Deno Dep&#xff08;原Deno包管理器&#xff09;彻底重构了JavaScript/TypeScript的依赖管理方式&#xff0c;其核心突破体现在&#xff1a; 1. 浏览器优先的模块化&#xff08;URL-Centric Modules&#xff09; // 直…

欧拉系统升级openssh 9.7p1

开发的系统准备上线&#xff0c;甲方对欧拉服务器进行了扫描&#xff0c;发现openssh版本为8.2p1&#xff0c;存在漏洞&#xff0c;因此需要升级openssh至9.7p1。欧拉系统版本为20.03 SP3。 1、下载openssh 9.7p1 https://www.openssh.com/releasenotes.html&#xff0c; 将下…

如何精通C++编程?

如果从学生时代算起的话&#xff0c;我学习和使用C已经差不多快十年了&#xff0c;仍然不敢说自己已经掌握了C的全部特性&#xff0c;但或许能够给出一些有用的建议吧。 我学习C全靠自学&#xff0c;花费了不少的功夫&#xff0c;在这里分享一些学习心得&#xff0c;希望对大家…

提高Qt工作线程的运行速度

1. 使用线程池&#xff08;QThreadPool&#xff09;替代单一线程 做过&#xff0c;但是当时没想到。。。 目的&#xff1a;减少线程创建和销毁的开销&#xff0c;复用线程资源。 实现步骤&#xff1a; 创建自定义任务类&#xff1a;继承QRunnable&#xff0c;实现run()方法。…

Solon AI MCP Server 入门:Helloworld (支持 java8 到 java24。国产解决方案)

目前网上能看到的 MCP Server 基本上都是基于 Python 或者 nodejs &#xff0c;虽然也有 Java 版本的 MCP SDK&#xff0c;但是鲜有基于 Java 开发的。 作为Java 开发中的国产顶级框架 Solon 已经基于 MCP SDK 在进行 Solon AI MCP 框架开发了&#xff0c;本文将使用 Solon AI …

STL之迭代器(iterator)

迭代器的基本概念 迭代器(iterator)模式又称为游标(Cursor)模式&#xff0c;用于提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。或者这样说可能更容易理解&#xff1a;Iterator模式是运用于聚合对象的一种模式&#xff0c;通过运用该模式&#…

Android系统通知机制深度解析:Framework至SystemUI全链路剖析

1. 前言 在Android 13的ROM定制化开发中&#xff0c;系统通知机制作为用户交互的核心组件&#xff0c;其实现涉及Framework层到SystemUI的复杂协作。本文将深入剖析从Notification发送到呈现的全链路流程&#xff0c;重点解析关键类的作用机制及系统服务间的交互逻辑&#xff…

UE5角色状态机中跳跃落地移动衔接问题

UE5系列文章目录 文章目录 UE5系列文章目录前言一、状态机设置二、主要蓝图 前言 先说说遇到的问题&#xff0c;在我按空格键跳跃落地以后&#xff0c;角色落地再按WSAD键移动就出现了画面中角色抽搐的情况 一、状态机设置 在Unreal Engine 5中创建角色时&#xff0c;处理跳…

使用SVM对心脏数据是否患病进行分类预测

作者简介 杜嘉宝&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2024级研究生 研究方向&#xff1a;变压器故障预警与检测 电子邮件&#xff1a;djb857497378gmail.com 王子谦&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2024级研究生&a…

Node做BFF中间层架构优化前端开发体验并提升系统整体性能。

文章目录 1. BFF 层的定位2. 技术选型3. 架构设计3.1 分层设计3.2 示例架构 4. 核心功能实现4.1 数据聚合4.2 权限校验4.3 缓存优化 5、实战示例1. 场景说明2. ECharts 数据格式要求3. BFF 层实现步骤3.1 接收前端参数3.2 调用后端服务获取数据 4. 前端使用 总结 在使用 Node.j…

文件系统 软硬连接

&#x1f33b;个人主页&#xff1a;路飞雪吖~ &#x1f320;专栏&#xff1a;Linux 目录 一、理解文件系统 &#x1f320;磁盘结构 二、软硬连接 &#x1f31f;软硬链接 &#x1f320;软链接&#xff1a; &#x1f320;硬链接&#xff1a; &#x1f31f;理解软硬链接的应…

单片机 | 基于51单片机的自动循迹小车设计

以下是一个基于51单片机的自动循迹小车设计详解,包含原理、公式和完整代码: 一、系统原理 核心模块: 传感器:红外对管(TCRT5000)x4主控芯片:STC89C52RC(51单片机)电机驱动:L298N驱动模块电源:7.4V锂电池(电机) + 5V稳压(单片机)工作原理: 红外对管发射红外线,…

2025.04.17【Stacked area】| 生信数据可视化:堆叠区域图深度解析

文章目录 生信数据可视化&#xff1a;堆叠区域图深度解析堆叠面积图简介为什么使用堆叠面积图如何使用R语言创建堆叠面积图安装和加载ggplot2包创建堆叠面积图的基本步骤示例代码 解读堆叠面积图堆叠面积图的局限性实际应用案例示例&#xff1a;基因表达量随时间变化 结论 生信…