基於canvas的二維碼邀請函生成插件

去年是最忙碌的一年,實在沒時間寫博客了,看着互聯網行業中一個又一個人的倒下,奉勸大家,健康要放在首位,保重身體。
好了,言歸正傳,這是17年的第一篇博文,話說這天又是產品同學跑過來問我說:hi,lenny,你看現在市面上流行各種裝逼H5,隨便輸入點名字啥的就給我生成房產證了,這種還可以分享出去,傳播率可高了,或者你再看這裏,一鍵生成邀請函,牛逼吧,要不你也幫我做一個這個功能,我去玩點傳播手段。

 

我看見效果後第一反映就是,肯定canvas進行的圖片拼接,現在市面上流行的效果具體是如何實現的我沒有去看源碼,思路很清晰,於是晚飯後沒有下班,開始我的插件製作之旅了。

 首先,我們需要思考,既然是圖片處理,那麼就必然存在圖片下載,我們知道圖片的onload是異步回調,所有的資源必須在下載完成後纔可以進行接下來的邏輯,前置資源下載的邏輯就很關鍵,我們不僅需要在onload事件回調後去處理我們後續的流程,同時需要在所有必須資源加載完成後才執行,所以我們需要構建一個資源數組大致如下:

[{
       {
            name: 'bg',
            src: '../img/bg.jpg'
        }, {
            name: 'z',
            src: '../img/z.png'
        }]

爲了獲得最終的complete事件,我們需要利用一個全局變量監聽onload或者onerror次數:

            var i = 1;
            arr.forEach(function(obj, index, array) {
                function onLoad() {
                    _self[obj.name] = img;
                    if (i < array.length) {
                        ++i;
                    } else {
                        console.log('complete');
                    };
                }
                var img = new Image();
                img.onload = onLoad;
                img.onerror = onLoad;
                img.src = obj.src;

好了,資源加載完成事件我們得到了,可以繼續下面的邏輯,既然是基於canvas,當然需要創建並初始化我們的canvas,我根據自己的需求,這個功能在我所使用的項目中不論初始化多少次,只會存在一個,所以我做了如下的控制:

init: function() {
            var LCanvasImg_canvas = document.querySelector('#LCanvasImg_canvas');
            if (LCanvasImg_canvas) {
                LCanvasImg_canvas.width = this.params.cw;
                LCanvasImg_canvas.height = this.params.ch;
                LCanvasImg_canvas.style.display = this.params.display;
                this.canvas = LCanvasImg_canvas;
            } else {
                var canvas = document.createElement('canvas');
                canvas.id = 'LCanvasImg_canvas';
                canvas.width = this.params.cw;
                canvas.height = this.params.ch;
                canvas.style.display = this.params.display;
                document.body.appendChild(canvas);
                this.canvas = canvas;
            }
            this.clear();
        },

canvas創建好了,接下來我們需要實現圖片渲染的能力,canvas的圖片渲染使用的是drawImage方法,根據官方文檔,該方法有3種傳參方式:

JavaScript 語法 1
在畫布上定位圖像:
context.drawImage(img,x,y);
JavaScript 語法 2
在畫布上定位圖像,並規定圖像的寬度和高度:
context.drawImage(img,x,y,width,height);
JavaScript 語法 3
剪切圖像,並在畫布上定位被剪切的部分:
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);

於是,我們也充分的判斷好我們調用的drawImage參數:

addImg: function(obj, callback) {
            var _self = this;
            var canvas = _self.canvas;
            var ctx = canvas.getContext("2d");
            if (obj.hasOwnProperty('sx') && obj.hasOwnProperty('sy') && obj.hasOwnProperty('sw') && obj.hasOwnProperty('sh') && obj.hasOwnProperty('x') && obj.hasOwnProperty('y') && obj.hasOwnProperty('width') && obj.hasOwnProperty('height')) {
                ctx.drawImage(_self[obj.name], obj.sx, obj.sy, obj.sw, obj.sh, obj.x, obj.y, obj.width, obj.height);
            } else if (obj.hasOwnProperty('x') && obj.hasOwnProperty('y') && obj.hasOwnProperty('width') && obj.hasOwnProperty('height')) {
                ctx.drawImage(_self[obj.name], obj.x, obj.y, obj.width, obj.height);
            } else if (obj.hasOwnProperty('x') && obj.hasOwnProperty('y')) {
                ctx.drawImage(_self[obj.name], obj.x, obj.y);
            } else {
                ctx.drawImage(_self[obj.name], 0, 0);
            }
            _self.showImg();
        },

接下來我們需要開發文字生成的能力,這個比較簡單,如果對canvas相關api熟悉點的,這部分沒有難度:

addFont: function(obj) {
            var _self = this;
            var canvas = _self.canvas;
            var ctx = canvas.getContext("2d");
            ctx.font = obj.fontsize + "px " + obj.fontfamily; //文字的字體大小和字體系列
            var ftop = obj.ftop; //文字top
            var fleft = obj.fleft; //文字left
            ctx.textBaseline = "top"; //設置繪製文本時的文本基線。
            ctx.fillText(obj.txt, fleft, ftop);
            ctx.lineWidth = 1;
            ctx.fillStyle = "#000";
            ctx.strokeStyle = "rgba(255,255,255,0.4)";
            ctx.strokeText(obj.txt, fleft, ftop);
        },

最後一步是二維碼的生成,這個有點坑,自己開發肯定來不及了,我選用的是一個開源插件:qrcode,根據這個插件,我們可以在一個img中動態生成二維碼的base64字串,而有了這個字串,我們也很方便的將內容輸出到我們的canvas中,爲了保證體驗,這個插件的最外層div直接display:none,避免它干擾到我們的實際項目。

<div id="qrcode" style="display: none;"></div>
/**
     * 
     * 初始化二維碼生成插件
     * 
     */
    var qrdata = '';
    var myqr = document.querySelector('#myqr');
    var qrcode = document.querySelector('#qrcode');
    var qr = new QRCode(qrcode, {
        width: 300,
        height: 300,
        colorDark: "#000000",
        colorLight: "#ffffff",
        correctLevel: QRCode.CorrectLevel.L
    });

由於這個img是動態變化的,我們獲取base64字串的時候一定要在該img的onload事件的回調內去獲取,這點非常重要:

function buildQr () {
    var img = qrcode.querySelector('img');
    img.onload = function() {
        qrdata = img.src;
        main();
    };
    qr.makeCode(myqr.value);
    }

ok,準備工作都完成了,接下來我們需要開始初始化我們的插件了,我預先埋下了很多可配置的參數:

var canvasImg = null;

    function main() {
        //初始化
        canvasImg = new LCanvasImg({
            cw: 768,//canvas width
            ch: 1163,//canvas height
            iw: '100%',//output img width
            ih: 'auto',//output img height
            display:'none'//canvas display
        });
        //資源加載
        canvasImg.load([{
            name: 'qr',
            src: qrdata
        }, {
            name: 'bg',
            src: '../img/bg.jpg'
        }, {
            name: 'z',
            src: '../img/z.png'
        }], build);
    };

看見上面的build變量了嗎?我們將圖片生成邏輯全部寫在這個build方法中,在load資源complete後,會執行build;

function build() {
        var farr = [{
            txt: document.querySelector('#mytxt1').value,
            fontsize: 26,
            fontfamily: 'fzjt',
            ftop: 140,
            fleft: 194
        }, {
            txt: '胡鑫',
            fontsize: 26,
            fontfamily: 'fzjt',
            ftop: 220,
            fleft: 394
        }, {
            txt: '鄧逸昕',
            fontsize: 26,
            fontfamily: 'fzjt',
            ftop: 220,
            fleft: 294
        }, {
            txt: document.querySelector('#mytxt1').value,
            fontsize: 26,
            fontfamily: 'fzjt',
            ftop: 220,
            fleft: 194
        }];
        canvasImg.addImg({
            name: 'bg',
            x: 0,
            y: 0,
            width: 768,
            height: 1163
        });
        farr.forEach(function(obj) {
            canvasImg.addFont(obj);
        });
        canvasImg.addImg({
            name: 'z',
            x: 0,
            y: 0,
            width: 100,
            height: 100
        });
        canvasImg.addImg({
            name: 'z',
            sx: 0,
            sy: 0,
            sw: 150,
            sh: 150,
            x: 100,
            y: 100,
            width: 100,
            height: 100
        });
        canvasImg.addImg({
            name: 'qr',
            x: 400,
            y: 800,
            width: 200,
            height: 200
        });
    };
    window.onload = buildQr;

最後一句話非常重要,爲什麼這裏我需要用window.onload事件,如果你使用的是webfont,當webfont下載成功後,其實還有一小段時間需要將font字體載入進瀏覽器中,只有在window.onload事件時,webfont字體文件才能生效。
最後奉上效果截圖:

 

整個demo已經上傳至github上了,如果需要做類似需求的同學可以下載該插件,可以節約大家許多時間
資源地址:https://github.com/xfhxbb/LCanvasImg

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