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

相关推荐
程序猿小玉兒2 分钟前
若依框架免登陆、页面全屏显示、打开新标签页(看板大屏)
前端
小薛博客21 分钟前
3、整合前端基础交互页面
java·前端·ai·交互
@蓝莓果粒茶26 分钟前
LeetCode第158题_用Read4读取N个字符 II
前端·c++·python·算法·leetcode·职场和发展·c#
天天扭码27 分钟前
【硬核教程】从入门到入土!彻底吃透 JavaScript 中 this 关键字这一篇就够了
前端·javascript·面试
csdn_HPL28 分钟前
SpringBoot + Vue 实现云端图片上传与回显(基于OSS等云存储)
vue.js·spring boot·后端
Mintopia1 小时前
计算机图形学学习指南
前端·javascript·计算机图形学
Mintopia1 小时前
three.js 中的动画(animation)
前端·javascript·three.js
AI大模型顾潇1 小时前
[特殊字符] Prompt如何驱动大模型对本地文件实现自主变更:Cline技术深度解析
前端·人工智能·llm·微调·prompt·编程·ai大模型
苹果酱05671 小时前
Vue3 源码解析(六):响应式原理与 reactive
java·vue.js·spring boot·mysql·课程设计
小小小小宇1 小时前
React中 useEffect和useLayoutEffect源码原理
前端