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

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

项目背景与需求分析

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

技术栈选择

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

代码实现详解

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

css 复制代码
* {
    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>部分)

html 复制代码
<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>部分)

javascript 复制代码
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 项目中实现了一个功能完整、交互良好的二维码合成与下载模块。希望本文能为你的前端开发实践带来启发,在实际项目中灵活运用相关技术!

相关推荐
x-cmd3 分钟前
[250512] Node.js 24 发布:ClangCL 构建,升级 V8 引擎、集成 npm 11
前端·javascript·windows·npm·node.js
夏之小星星16 分钟前
el-tree结合checkbox实现数据回显
前端·javascript·vue.js
crazyme_630 分钟前
前端自学入门:HTML 基础详解与学习路线指引
前端·学习·html
撸猫79138 分钟前
HttpSession 的运行原理
前端·后端·cookie·httpsession
亦世凡华、1 小时前
Rollup入门与进阶:为现代Web应用构建超小的打包文件
前端·经验分享·rollup·配置项目·前端分享
琉璃℡初雪1 小时前
vue2/3 中使用 @vue-office/docx 在网页中预览(docx、excel、pdf)文件
vue.js·pdf·excel
Bl_a_ck1 小时前
【React】Craco 简介
开发语言·前端·react.js·typescript·前端框架
augenstern4162 小时前
webpack重构优化
前端·webpack·重构
海拥✘2 小时前
CodeBuddy终极测评:中国版Cursor的开发革命(含安装指南+HTML游戏实战)
前端·游戏·html
寧笙(Lycode)3 小时前
React系列——HOC高阶组件的封装与使用
前端·react.js·前端框架