基于canvas的二維碼邀請(qǐng)函生成插件
這是17年的第一篇博文,話說這天又是產(chǎn)品同學(xué)跑過來問我說:hi,lenny,你看現(xiàn)在市面上流行各種裝逼H5,隨便輸入點(diǎn)名字啥的就給我生成房產(chǎn)證了,這種還可以分享出去,傳播率可高了,或者你再看這里,一鍵生成邀請(qǐng)函,牛吧,要不你也幫我做一個(gè)這個(gè)功能,我去玩點(diǎn)傳播手段。

我看見效果后第一反映就是,肯定canvas進(jìn)行的圖片拼接,現(xiàn)在市面上流行的效果具體是如何實(shí)現(xiàn)的我沒有去看源碼,思路很清晰,于是晚飯后沒有下班,開始我的插件制作之旅了。
首先,我們需要思考,既然是圖片處理,那么就必然存在圖片下載,我們知道圖片的onload是異步回調(diào),所有的資源必須在下載完成后才可以進(jìn)行接下來的邏輯,前置資源下載的邏輯就很關(guān)鍵,我們不僅需要在onload事件回調(diào)后去處理我們后續(xù)的流程,同時(shí)需要在所有必須資源加載完成后才執(zhí)行,所以我們需要構(gòu)建一個(gè)資源數(shù)組大致如下:
[{
{
name: 'bg',
src: '../img/bg.jpg'
}, {
name: 'z',
src: '../img/z.png'
}]為了獲得最終的complete事件,我們需要利用一個(gè)全局變量監(jiān)聽onload或者onerror次數(shù):
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;好了,資源加載完成事件我們得到了,可以繼續(xù)下面的邏輯,既然是基于canvas,當(dāng)然需要?jiǎng)?chuàng)建并初始化我們的canvas,我根據(jù)自己的需求,這個(gè)功能在我所使用的項(xiàng)目中不論初始化多少次,只會(huì)存在一個(gè),所以我做了如下的控制:
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創(chuàng)建好了,接下來我們需要實(shí)現(xiàn)圖片渲染的能力,canvas的圖片渲染使用的是drawImage方法,根據(jù)官方文檔,該方法有3種傳參方式:
JavaScript 語法 1
在畫布上定位圖像:
context.drawImage(img,x,y);
JavaScript 語法 2
在畫布上定位圖像,并規(guī)定圖像的寬度和高度:
context.drawImage(img,x,y,width,height);
JavaScript 語法 3
剪切圖像,并在畫布上定位被剪切的部分:
context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height);
于是,我們也充分的判斷好我們調(diào)用的drawImage參數(shù):
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();
},接下來我們需要開發(fā)文字生成的能力,這個(gè)比較簡(jiǎn)單,如果對(duì)canvas相關(guān)api熟悉點(diǎn)的,這部分沒有難度:
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"; //設(shè)置繪制文本時(shí)的文本基線。
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);
},最后一步是二維碼的生成,這個(gè)有點(diǎn)坑,自己開發(fā)肯定來不及了,我選用的是一個(gè)開源插件:qrcode,根據(jù)這個(gè)插件,我們可以在一個(gè)img中動(dòng)態(tài)生成二維碼的base64字串,而有了這個(gè)字串,我們也很方便的將內(nèi)容輸出到我們的canvas中,為了保證體驗(yàn),這個(gè)插件的最外層div直接display:none,避免它干擾到我們的實(shí)際項(xiàng)目。
<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
});由于這個(gè)img是動(dòng)態(tài)變化的,我們獲取base64字串的時(shí)候一定要在該img的onload事件的回調(diào)內(nèi)去獲取,這點(diǎn)非常重要:
function buildQr () {
var img = qrcode.querySelector('img');
img.onload = function() {
qrdata = img.src;
main();
};
qr.makeCode(myqr.value);
}ok,準(zhǔn)備工作都完成了,接下來我們需要開始初始化我們的插件了,我預(yù)先埋下了很多可配置的參數(shù):
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變量了嗎?我們將圖片生成邏輯全部寫在這個(gè)build方法中,在load資源complete后,會(huì)執(zhí)行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,當(dāng)webfont下載成功后,其實(shí)還有一小段時(shí)間需要將font字體載入進(jìn)瀏覽器中,只有在window.onload事件時(shí),webfont字體文件才能生效。
最后奉上效果截圖:

整個(gè)demo已經(jīng)上傳至github上了,如果需要做類似需求的同學(xué)可以下載該插件,可以節(jié)約大家許多時(shí)間
資源地址:https://github.com/xfhxbb/LCanvasImg
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
微信小程序中做用戶登錄與登錄態(tài)維護(hù)的實(shí)現(xiàn)詳解
微信小程序的運(yùn)行環(huán)境不是在瀏覽器下運(yùn)行的。所以不能以cookie來維護(hù)登錄態(tài)。下面這篇文章主要給大家介紹了微信小程序中如何做用戶登錄與登錄態(tài)維護(hù)的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考學(xué)習(xí)。2017-05-05
ES6 Promise對(duì)象概念及用法實(shí)例詳解
這篇文章主要介紹了ES6 Promise對(duì)象概念及用法,結(jié)合實(shí)例形式詳細(xì)分析了ES6中Promise對(duì)象的概念、原理、創(chuàng)建、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2019-10-10
響應(yīng)式表格之固定表頭的簡(jiǎn)單實(shí)現(xiàn)
下面小編就為大家?guī)硪黄憫?yīng)式表格之固定表頭的簡(jiǎn)單實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08
flash調(diào)用js中的方法,讓js傳遞變量給flash的辦法及思路
前幾天發(fā)表了 將FlashVars寫在JS函數(shù)中,實(shí)現(xiàn)與后臺(tái)的實(shí)時(shí)變量更新,但是僅支持 IE,隨后與 Luckyer 進(jìn)行了交流,發(fā)現(xiàn)用 SetVariable 可以很方便的實(shí)現(xiàn)多瀏覽器兼容。舉例如下。2013-08-08
js GridView 實(shí)現(xiàn)自動(dòng)計(jì)算操作代碼
js操作GridView,實(shí)現(xiàn)自動(dòng)計(jì)算的實(shí)現(xiàn)代碼,下面的代碼運(yùn)行即可2009-03-03
js實(shí)現(xiàn)帶關(guān)閉按鈕始終顯示在網(wǎng)頁最底部工具條的方法
這篇文章主要介紹了js實(shí)現(xiàn)帶關(guān)閉按鈕始終顯示在網(wǎng)頁最底部工具條的方法,是非常實(shí)用的javascript固定效果,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03
常用js,css文件統(tǒng)一加載方法(推薦) 并在加載之后調(diào)用回調(diào)函數(shù)
下面小編就為大家?guī)硪黄S胘s,css文件統(tǒng)一加載方法(推薦) 并在加載之后調(diào)用回調(diào)函數(shù)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09
Electron去掉窗口邊框并添加關(guān)閉按鈕的實(shí)現(xiàn)步驟
在?Electron?中,如果你想去掉默認(rèn)的窗口邊框(frame)并添加額外的按鍵,可以通過相關(guān)步驟實(shí)現(xiàn),下面小編給大家?guī)砹薊lectron去掉窗口邊框并添加關(guān)閉按鈕的實(shí)現(xiàn)步驟,感興趣的朋友一起看看吧2024-06-06
JavaScript類的繼承方法小結(jié)【組合繼承分析】
這篇文章主要介紹了JavaScript類的繼承方法,結(jié)合實(shí)例形式總結(jié)分析了JavaScript繼承的概念、原理及組合繼承相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-07-07

