最近公司业务有一个需求,在页面上需要将一个URL转为二维码并配上文字说明,并提供一个下载按钮,可以下载带有文字说明的二维码图片。
如果说只要一个二维码图片,Github上的qrcodejs库就能帮我们实现;
但是如果再加上文字说明,情况就变得有那么一丝复杂。
王二之前尝试过直接在canvas里绘画文字,但是canvas里文字不会换行,虽然可以通过js控制每行文字的数量来换行,但是文字有宽有窄,终究不是一个好的解决方案;
然后又上网找解决方案,然后听说用svg可以直接将一个div整个画出来。王二也尝试了,svg确实可以将div整个画出来,再导入到canvas里。这时候二维码和文字说明都在一张canvas里,而且文字实际上可以用css的样式来控制。但是在最后一步,将canvas转换为base64再导出图片的时候栽了跟头,在canvas里用svg画出来的部分最后转为图片的时候不会呈现,这条思路似乎又走不通了。
然后又上网找解决方案,最后在Github发现了html2canvas这个神奇的库,它可以将一个div绘画成canvas,那么思路也随之而来了。
王二写了如下的一串html代码,并对 qrcode
、html2canvas
进行了一个简单的封装,以下代码可以直接运行,有兴趣可以直接拷贝到本地看看运行效果:
<html>
<head>
<title>title>
<meta charset="utf-8"/>
head>
<body>
<div id="QRcode">div>
<a class="QR-download" href="#" download>下载a>
<script src="http://www.wangyulue.com/assets/js/qrcode.js">script>
<script src="http://www.wangyulue.com/assets/js/html2canvas.min.js">script>
<script>
/**
* 输出一个带有文字说明的二维码。
*
* @param {Object} obj 相关配置,
* obj = {
dom : "QRcode", //
url : "http://www.wangyulue.com", //二维码网址
text : ["王玉略的个人网站","Stay Hungry, Stay Foolish."], //二维码的文字说明
pic_size : 300, // 图片的尺寸大小
font_size : 16, //说明文字的字体大小
}
* 回调函数中返回一个base64形式的图片,以便在回调中注入到标签中下载使用
*
*/
function getQRcodeImg(obj,fn){
var url = obj.url,
size = obj.pic_size || 256;
font_size = obj.font_size || 18;
dom = document.getElementById(obj.dom);
dom.innerHTML = generateHTML(obj.text,size,font_size);
var $qrcode = dom.getElementsByClassName("QR-qrcode")[0],
$main = dom.getElementsByClassName("QR-main")[0];
new QRCode($qrcode, {
text: url,
width: size,
height: size,
});
html2canvas($main).then(function(canvas) {
var base64 = concatCanvas(dom,$qrcode.getElementsByTagName("canvas")[0],canvas,20);
fn && fn(base64);
});
/**
* 根据配置拼凑要处理的html代码
*
* @param {Array} arr 要添加的文字,以数组传入
*
*/
function generateHTML(arr,size,font_size){
var out = "",temp = "";
arr.forEach(function(item){
temp += ""+ item +"" ;
})
out =
"" +
"" +
temp +
"" +
"" +
"";
return out ;
}
/**
* 将两个canvas合并在一起
*
* @param {Object} dom canvas要添加的dom对象
* @param {Object} canvas1 第一个canvas
* @param {Object} canvas2 第二个canvas
* @param {Number} padding 图片的padding,默认20
* @return {String} 返回base64字符串
*
*/
function concatCanvas(dom,canvas1,canvas2,padding){
var c1h = canvas1.height,
c1w = canvas1.width,
// c2h = canvas2.height,
// c2w = canvas2.width,
/* 说明:将以上两行代码改为以下两行代码;
* 原因:以上两行代码在window下显示是好的,但是放到Mac下显示会出一些问题,
* 仔细发现是在Mac下,html2canvas的API返回的canvas不符合预期,在MAC环境canvas返回如下:
* html2canvas($main).then(function(canvas) {
* console.log(canvas); //
* });
* 可以看到看到canvas的width和height值和其style里的width和height的值有所不同,
* 于是采用以下的方法来获得canvas的宽度和高度
* 王二认为出现以上问题可能是因为分辨率的问题
*/
c2h = Number(canvas2.style.height.slice(0,-2));
c2w = Number(canvas2.style.width.slice(0,-2));
canvas = document.createElement("canvas");
padding = padding || 20 ;
/** 根据二维码、文字两个canvas,再加上padding计算出新的canvas的宽度和高度 **/
canvas.height = c1h + c2h + 2.5 * padding ;
canvas.width = Math.max(c1w,c2w) + 2 * padding ;
/** end **/
dom.appendChild(canvas);
/** 将canvas画上白色背景 **/
context = canvas.getContext("2d");
context.fillStyle ="white";
context.fillRect(0,0,canvas.width,canvas.height);
/** end **/
/** 将二维码、文字两个canvas绘画到一个canvas里 **/
context.drawImage(canvas1, padding, padding, c1w ,c1h);
context.drawImage(canvas2, padding, 1.5 * padding+c1h, c2w ,c2h);
/** end **/
/** 返回base64,用于注入到a标签里以便下载 **/
return canvas.toDataURL('image/jpeg',1);
/** end **/
}
}
var obj = {
dom : "QRcode",
url : "http://www.wangyulue.com",
text : ["王玉略的个人网站","Stay Hungry, Stay Foolish."],
pic_size : 300,
font_size : 16,
}
getQRcodeImg(obj,function(base64){
document.getElementsByClassName("QR-download")[0].href = base64 ;
});
script>
body>
html>
复制代码
下载后的图片就像下面这样:
对于很长的文字,它也能很完美的换行,并且图片的高度自适应,就像下面这样:
这样来看,这个需求算是被完美解决了。
原文发表于 王玉略的个人网站