如何基於QRCode製作二維碼插件(一)支持三色漸變,中心LOGO圖標

QRCode

製作一款適合自己使用的QRCode插件,基於QRCode代碼更改

qrcode源碼:https://github.com/davidshimjs/qrcodejs

比起qrcode擴展的功能的有

  • Css選擇器(Qrcode僅支持id選擇器)
  • 二維碼背景顏色漸變
  • 支持中心圖標(可調整圖片的大小)

QRCode是如何製作二維碼

三種方式:

  • Canvas :繪製成功後生成img標籤,可通過css控制樣式

  • SVG:

    需添加useSVG:ture屬性,生成的二維碼是一張 svg 矢量圖 無img標籤

  • Table構造點陣

var useSVG = document.documentElement.tagName.toLowerCase() === "svg";
// Drawing in DOM by using Table tag
var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () { ... }

這段代碼說的是先判斷是否支持SVG,如果支持則使用SVG;否則判斷是否支持Canvas,如果支持則使用Canvas,如果不支持,就使用Table來構造點陣

插件擴展功能是如何實現

兩種實現方式(只針對canvas生成的二維碼)

一種是純畫Canvas,沒有img標籤,所有的屬性只能通過代碼控制,無法通過css控制

一種是利用canvas生成兩張圖片,一張二維碼,一張中心圖標,中心圖標覆蓋在二維碼上方,可通過css控制

中心圖標的生成

可直接參考https://blog.csdn.net/chy555chy/article/details/85785819的文章

可通過iconWidth ,iconHeight設置icon的大小

但這個方法是純畫Canvas的

QRCode = function (el, vOption) {
		const ratio = 0.3;
		this._htOption = {
			width : 256,
			height : 256,
			typeNumber : 4,
			colorDark : "#000000",
			colorLight : "#ffffff",
		};
        this._htOption.iconWidth=this._htOption.width * ratio;
        this._htOption.iconHeight=this._htOption.width * ratio;
     }

定義默認的圖標大小爲二維碼的三分之一,默認爲正方形的圖標

        /**
         * Draw the icon of QR code Center
         * @param {QRCode} oQRCode
         */
        Drawing.prototype.addIcon =  async function (iconSrc) {
            //通過ES6的async/await語法將異步的Promise轉爲同步方法
            const image = await new Promise((resolve, reject) => {
                const image = new Image();
            image.src = iconSrc;
            image.crossOrigin='Anonymous';
            image.onload=()=>{resolve(image);};
        });
            const ratio  = 0.3;//三分之二開始
            const marginRatio = (1-ratio)/2;
            const x = this._htOption.width * marginRatio;
            const y = this._htOption.height * marginRatio;
            const width = this._htOption.iconWidth ;
            const height = this._htOption.iconHeight;
            this._oContext.beginPath();
            this._oContext.arc(x , y ,0, Math.PI * 3 / 2, Math.PI * 2);
            this._oContext.lineTo(width + x, y);
            this._oContext.arc(width + x,  y,0, Math.PI * 3 / 2, Math.PI * 2);
            this._oContext.lineTo(width + x, height + y );
            this._oContext.arc(width + x, height + y,0, Math.PI * 3 / 2, Math.PI * 2);
            this._oContext.lineTo( x, height +y);
            this._oContext.arc(x, height + y,0, Math.PI * 3 / 2, Math.PI * 2);
            this._oContext.closePath();
            this._oContext.save();
            this._oContext.clip();
            this._oContext.drawImage(image, x, y, width, height);
            this._oContext.restore();
        };

但是上訴代碼有個問題

生成的圖標並非是位於二維碼的中心,而是從3分之2處開始

在這裏插入圖片描述

圖標位於中心需要重新定位x,y座標

            //起始座標
            const x = (this._htOption.width -this._htOption.iconWidth) / 2;
            //終止座標
            const y = (this._htOption.height - this._htOption.iconHeight) / 2;

在這裏插入圖片描述

顏色漸變

qrcode設置顏色的代碼如下

for (var row = 0; row < nCount; row++) {
    for (var col = 0; col < nCount; col++) {
       var bIsDark = oQRCode.isDark(row, col);
       var nLeft = col * nWidth;
       var nTop = row * nHeight;
       //根據isDark的判斷繪製固定顏色
       _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
       _oContext.lineWidth = 1;
       _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight;
       _oContext.fillRect(nLeft, nTop, nWidth, nHeight);
       //防滑處理 ...
       }
    }

因爲漸變是一個顏色,本來以爲需要計算每行每列的固定顏色,也就是說需要知道初始顏色,到末尾顏色的差值,然後除以每行每列變化的閾值,加註在初始顏色上,這樣每個像素的顏色纔會不同

這樣實現起來真的太麻煩,首先是#dddddd顏色16進制的運算,然後是方向,如果只有一個方向還好說,可以允許設置方向的,計算方式又不同

幸好!

畫布的筆觸是針對整體而言,並非如上訴代碼針對某個像素,可通過設置一個顏色漸變的筆觸達到效果

var gradient=ctx.createLinearGradient(0,0,170,0);
gradient.addColorStop("0","magenta");
gradient.addColorStop("0.5","blue");
gradient.addColorStop("1.0","red");

 _oContext.strokeStyle = bIsDark ? gradient : _htOption.colorLight;
                        _oContext.lineWidth = 1;
                        _oContext.fillStyle = bIsDark ? gradient : _htOption.colorLight;
                        _oContext.fillRect(nLeft, nTop, nWidth, nHeight);

整理之後的代碼

可通過linearGradientDirection設置方向,linearGradientFrom起始顏色,linearGradientTo終止顏色,linearGradientMiddle中間顏色

方向常量

    var QRGradientDirection = {
        LEFT: 'to left', 
        RIGHT: 'to right',
        TOP: 'to top',
        TOPRIGHT:'to top right',
        TOPLEFT:'to top left',
        BOTTOM: 'to bottom',
        BOTTOMRIGHT:'to bottom right',
        BOTTOMLEFT:'to bottom left'
    };

根據方向生成不同的漸變對象

/**
 * Different gradient objects are generated according to the incoming gradient direction
 * @constructor zoe
 * @param directionType Gradient direction type
 * @returns {CanvasGradient}
 */
Drawing.prototype.getDirection = function(directionType){
    var _oContext = this._oContext;
    var _htOption = this._htOption;
    switch (directionType){
        case QRGradientDirection.LEFT:
            return _oContext.createLinearGradient(_htOption.width,0,0,0);
        case QRGradientDirection.TOP:
            return _oContext.createLinearGradient(0,_htOption.height,0,0);
        case QRGradientDirection.TOPLEFT:
            return _oContext.createLinearGradient(_htOption.width,_htOption.height,0,0);
        case QRGradientDirection.TOPRIGHT:
            return _oContext.createLinearGradient(0,_htOption.height,_htOption.width,0);
        case QRGradientDirection.BOTTOM:
            return _oContext.createLinearGradient(0,0,0,_htOption.height);
        case QRGradientDirection.BOTTOMLEFT:
            return _oContext.createLinearGradient(_htOption.width,0,0,_htOption.height);
        case QRGradientDirection.BOTTOMRIGHT:
            return _oContext.createLinearGradient(0,0,_htOption.width,_htOption.height);
        case QRGradientDirection.RIGHT:
        default:
            return _oContext.createLinearGradient(0,0,_htOption.width,0);
    }

默認爲黑色筆觸,如果配置linearGradient爲true 則 生成漸變筆觸

//Auther by zoe
//默認爲黑色
var gradient = _htOption.colorDark;

// 如果允許漸變
if(this._htOption.linearGradient){
    var gradient=this.getDirection(_htOption.linearGradientDirection);
    gradient.addColorStop("0",_htOption.linearGradientFrom);
    gradient.addColorStop("1.0",_htOption.linearGradientTo);
    if(_htOption.linearGradientMiddle){
        gradient.addColorStop("0.5",_htOption.linearGradientMiddle);
    }
}

// 純色二維碼
for (var row = 0; row < nCount; row++) {
    for (var col = 0; col < nCount; col++) {
        var bIsDark = oQRCode.isDark(row, col);
        var nLeft = col * nWidth;
        var nTop = row * nHeight;
        _oContext.strokeStyle = bIsDark ? gradient : _htOption.colorLight;
        _oContext.lineWidth = 1;
        _oContext.fillStyle = bIsDark ? gradient : _htOption.colorLight;
        _oContext.fillRect(nLeft, nTop, nWidth, nHeight);

        // 안티 앨리어싱 방지 처리 防滑處理
        _oContext.strokeRect(
            Math.floor(nLeft) + 0.5,
            Math.floor(nTop) + 0.5,
            nRoundedWidth,
            nRoundedHeight
        );

        _oContext.strokeRect(
            Math.ceil(nLeft) - 0.5,
            Math.ceil(nTop) - 0.5,
            nRoundedWidth,
            nRoundedHeight
        );
    }
}

代碼可通過npm i peachill-qrcode下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章