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

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax