纯前端实现自定义二维码背景图

起因

今天在前端群里划水,突然有人问了一个问题,想要纯前端去实现一个定制二维码背景的功能,总所周知,我们正常生成的二维码(QRcode生成)点阵的颜色是纯黑+纯白的。但是很多二维码的服务网站,生成的二维码却是十分炫酷的,相对于普通的二维码,这种美化过的二维码会更加吸引用户。那我们暂时停止划水,找找是否有纯前端实现的方式

调研

  1. 目前生成二维码最好的网站应该是草料二维码了,去草料二维码看看,生成个普通二维码,正常,然后修改二维码的颜色,这是发现前端调了接口,接口返回了二维码,所以草料的二维码生成逻辑是全放在后端的,前端只负责展示。
  2. 然后群友a说可以吧二维码黑色部分透明,然后把背景图叠在二维码下面,造成一个透视,看起来就像二维码颜色被修改了一样
  3. 受到a的启发,我想了下,如果可以把黑色的部分数据拿到,然后将黑色的部分按照对应的背景图的颜色进行填充,不是也行吗。

找到方法

2和3有思路了,那要如何去拿到图片颜色信息,并进行渲染修改呢,我突然想到前几周看了掘金的一篇生成点阵图的文章,里面有通过canvas进行图片修改的方法,主要是通过canvas渲染图片,然后拿到图片信息,然后进行像素级别的对比,更改颜色,重新绘制。

开始实践

刚开始想的时候感觉比较难,但是真的写完发现非常简单,本次我们所需要了解的几个点:

    1. canvas获取图片信息 ctx.getImageData(x, y, imgX, imgY)
    1. 图片信息data里储存的是像素信息,每四位为一个像素的信息,分别是 r,g,b,alpha, 前三位对应着这个像素点的颜色,最后一位是当前像素的不透明度
    1. 更新canvas的图片信息 ctx.putImageData(changeData, x, y)

代码如下:

export default class Qrcode2Color {
    constructor(options) {
        const {qrUrl, bgUrl, size} = options;
        // 二维码信息
        this.qrInfo = {
            url: qrUrl,
            imgData: null,
            canvas: null,
            ctx: null
        };
        // 背景图信息
        this.bgInfo = {
            url: bgUrl,
            imgData: null,
            canvas: null,
            ctx: null
        };
        // 指定canvas的宽高
        this.size = {
            width: 0, 
            height: 0,
            ...size,
        };
        this.init();
    }
    init() {
        this.createCanvas(this.qrInfo);
        this.createCanvas(this.bgInfo, false);
    }
    // 创建canvas
    createCanvas(info, isAppend = true) {
        info.canvas = document.createElement('canvas');
        isAppend && document.body.appendChild(info.canvas);
        info.ctx = info.canvas.getContext('2d');
        this.drawImage(info);
    }
    // 将图片绘制到canvas上,并获取图片信息
    drawImage(info) {
        const img = new Image();
        const {width, height} = this.size;
        img.src = info.url;
        img.onload = function() {
            info.canvas.width = width;
            info.canvas.height = height;
            info.ctx.drawImage(img, 0, 0);
            info.imgData = info.ctx.getImageData(0, 0, width, height);
        }
    }
    // 修改颜色,因为img.onload事件是异步,所以这里需要给个延迟执行
    changeColor() {
        this.timer = setTimeout(() => {
            if (this.timer) {
                clearTimeout(this.timer);
            }
            const qrData = this.qrInfo.imgData;
            const bgData = this.bgInfo.imgData;
            if ( qrData && bgData) {
                let imgDataLength = qrData.data.length;
                const imgSource = qrData.data;
                let i;
                for(i = 0; i < imgDataLength; i++) {
                    if (i%4 === 0) {
                        if (!(imgSource[i] + imgSource[i+1] + imgSource[i+2])) {
                            imgSource[i] = bgData.data[i];
                            imgSource[i + 1] = bgData.data[i + 1];
                            imgSource[i + 2] = bgData.data[i + 2];
                            imgSource[i + 3] = bgData.data[i + 3];
                        }
                    }
                }
                // 更新canvas图片信息
                this.qrInfo.ctx.putImageData(qrData, 0, 0)
            }
        }, 400);
    }
}
复制代码

效果

image.png

完成

这个代码只是简单实现了普通二维码修改背景色的需求,但是真正的业务要比这个复杂的多,比如需要绕开某个区域,或者下载or上传修改过后的二维码。这些功能本次代码内没有体现。如果有思路,并且思路正确,实现起来应该也不难。

@qrcode2color

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享