近日要开发一个能将生成的二维码另存为图片的功能(该图片呢,肯定不止一个二维码,还包括背景、文字等其他元素),首先呢,就想到了html2canvas,随便一百度就是各种踩坑日志,我也随一下大流,记录本人在开发过程中遇到的坑。
上半部分纯属踩坑,如果想看解决办法,直接拉到最下面,后面有更新。
1.基本用法:
在html2canvas上找到了它的基本用法以及压缩包
<div id="capture" style="padding: 10px; background: #f5da55"> <h4 style="color: #000; ">Hello world!</h4> </div> html2canvas(document.querySelector("#capture")).then(canvas => { document.body.appendChild(canvas) });
还搜索到了另外一种用法:http://caibaojian.com/html2canvas.html
html2canvas(document.body, { onrendered: function(canvas) { var url = canvas.toDataURL();//图片地址 document.body.appendChild(canvas); }, width: 300, height: 300 });
坑1:
由于项目要兼容到ie8,前者还用到了promise,我想在ie里面不能完全兼容啊,所以当即就用后者来测试了一下,奇怪的是我的onrendered方法为什么不执行,看了一下html结构,好么,画的canvas闪现了一下就没了,没了,什么情况,我有点懵,是不是因为我的压缩包用得不对,然后我就去找另外版本的js,在这个网址终于找到了我想要的版本https://www.bootcdn.cn/html2canvas/ (之前用的官网的1.0.0-rc.5,换了0.5.0-beta4的版本),改了压缩包之后,我的图片愉快的被生成了。
坑2:
但是在ie10下还是出现了问题,说是promise未定义,那就想办法让它支持该语法呗,然后就搜到了引用bluebird.js可以使ie支持promise的语法,把该js用上之后终于不报错了。
2.下载
html2canvas(document.getElementById('buildImg'), { onrendered: function(canvas) { var url = canvas.toDataURL(); if (window.navigator && window.navigator.msSaveOrOpenBlob) { var bstr = atob(url.split(',')[1]) //atob与blob都是支持ie10+ var n = bstr.length var u8arr = new Uint8Array(n) while (n--) { u8arr[n] = bstr.charCodeAt(n) } var blob = new Blob([u8arr]) window.navigator.msSaveOrOpenBlob(blob, '图片下载.png'); return; }var a = document.createElement("a"); a.href = url; a.download = "图片下载"; document.body.appendChild(a); a.click(); document.body.removeChild(a); } });
上面的代码在谷歌与ie10都可以下载,但是ie9怎么办呢,花了大半天时间也没有想到解决办法。然后业务又说想把下载做成可以选择磁盘保存的方式,即模拟右键另存为。后面试了一下,在ie下是可以的(谷歌不行),但是需要图片url,而生成的图片是base64啊,并没有链接,如果想转成url链接应该还需要后端支持。想来想去太麻烦了,又与业务商量了下,要不先把图片生成放在那里吧,然后自己手动去右键另存为,上面再给点提示,业务同意了,这里的坑算是过了,上面的方法舍弃。
//假装外面有个请求:
if (data.type == "SUCCESS") { var url = 'data:image/png;base64,'+data.data.qrCode; $('#img').attr('src',url); $('.js-dialog').show(); html2canvas(document.getElementById('buildImg'), { onrendered: function(canvas) {
var url = canvas.toDataURL("image/png", 1.0);
$('#buildImg').html('<img src="'+url+'" width="100%;"/>')
} }) }
3.图片模糊
随后又发现在谷歌上生成的图片有点糊,看了网上一水的方法都是自定义canvas,那我就试试?
if (data.type == "SUCCESS") { var url = 'data:image/png;base64,'+data.data.qrCode; $('#img').attr('src',url); $('#copyLink').attr('data-clipboard-text',data.data.link); var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); var scale = 1; canvas.width = 320 * scale; canvas.height = 480 * scale; canvas.getContext("2d").scale(scale, scale); $('.js-dialog').show(); html2canvas(document.getElementById('buildImg'), { canvas: canvas, //自定义 canvas scale: scale, width:320, height:480, useCORS: true,
onrendered: function(canvas) {
var url = canvas.toDataURL("image/png", 1.0);
$('#buildImg').html('<img src="'+url+'" width="100%;"/>')
} }) }
随后我就发现生成的图片一片空白,我画的图去哪儿了?是不是onrendered又不生效了,之后我又用了promise语法
if (data.type == "SUCCESS") { var url = 'data:image/png;base64,'+data.data.qrCode; $('#img').attr('src',url); $('#copyLink').attr('data-clipboard-text',data.data.link); var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); var scale = 1; canvas.width = 320 * scale; canvas.height = 480 * scale; canvas.getContext("2d").scale(scale, scale); $('.js-dialog').show(); html2canvas(document.getElementById('buildImg'), { canvas: canvas, //自定义 canvas scale: scale, width:320, height:480, useCORS: true, }).then(function(canvas){ var url = canvas.toDataURL("image/png", 1.0); $('#buildImg').html('<img src="'+url+'" width="100%;"/>') }); }
额额,还是不对,是不是我的压缩包没用对,然后又换成了官网上copy的版本1.0.0-rc.5。咦,好像可以了,图片也清晰了。转了一圈还是用了最开始的办法,真想甩自己两大巴子。
4.图片偏移
虽然图片清晰了,但是会有10-20px的横向偏移,ie下不但横向偏移还会纵向偏移,下面是解决办法:
if (data.type == "SUCCESS") { var url = 'data:image/png;base64,'+data.data.qrCode; $('#img').attr('src',url); $('#copyLink').attr('data-clipboard-text',data.data.link); var canvas = document.createElement("canvas"); var context = canvas.getContext("2d"); var scale = 1; canvas.width = 320 * scale; canvas.height = 480 * scale; canvas.getContext("2d").scale(scale, scale); if ((window.navigator && window.navigator.msSaveOrOpenBlob) || isIe){ context.translate(-10,-20); }else{ context.translate(-10,0); } $('.js-dialog').show(); html2canvas(document.getElementById('buildImg'), { canvas: canvas, //自定义 canvas scale: scale, width:320, height:480, useCORS: true, }).then(function(canvas){ var url = canvas.toDataURL("image/png", 1.0); $('#buildImg').html('<img src="'+url+'" width="100%;"/>') }); }
以上就是本人在开发过程中遇到的坑,已经能完美兼容ie9+。
happy ending~~~
2022/7/22
之前的逻辑总是不尽人意,趁着业务需求更改,把代码也做下调整
1.dom结构如下
<div class="code-dialog js-dialog"> <div class="bg"></div> <div class="code-content"> <div class="title">我的内推码<span id="close">×</span></div> <p class="notice">请将鼠标移至图片,点击右键保存图片。</p> <div class="img-wrap" id="buildImg"> <img src="../../assets/images/cmbnt/recommend-bill.jpg" width="100%"> <div class="text-content"> <div class="qrcode"> <div class="code-title">内推码</div> <div class="code">YTYEYM</div> <img id="img" src="../../assets/images/qrcode.png" width="200"> </div> <p class="text">微信“扫一扫”参与内部推荐</p> </div> <div class="name">推荐人:xxx</div> </div> <div class="btn-wrap"> <button id="copyCode" data-clipboard-text="www.baidu.com">复制内推码</button> <button style="margin-left:6px;" id="copyLink" data-clipboard-text="www.baidu.com">复制链接</button> </div> </div> </div>
2.css样式
.code-dialog{ position:fixed; top:0; right:0; bottom:0; left:0; display:none; z-index: 999; } .code-dialog .bg{ position:absolute; top:0; left:0; bottom:0; right:0; background:#000; opacity: .3; filter: Alpha(opacity=30); } .code-dialog .code-content{ position:absolute; top:50%; left:50%; margin-left:-347px; margin-top:-429px; width:694px; height:857px; background:#fff; border-radius:8px; } .code-dialog *{box-sizing: border-box;} .code-content .title{ margin-bottom: 24px; padding:0 24px; font-size:16px; font-weight: bold; color: #5C6674; line-height:48px; background: #F7F8FC; border-radius: 8px 8px 0px 0px; } .code-content .title span{ float:right; display: inline-block; font-size: 26px; font-weight: normal; line-height: 48px; color: #999; cursor:pointer; } .text-c{text-align:center;} .code-content .img-wrap{ margin:0 auto; width:390px; height:665px; font-size:0; text-align:center; color: #757A8A; background:#fff; box-sizing: border-box; box-shadow: 0px 0px 12px 0px rgba(92, 102, 116, 0.3); } .code-content .code-title{ position: relative; margin-top:14px; display:inline-block; padding:0 10px; color:#9094A0; background:#fff; transform: scale(.8); -webkit-transform: scale(.8); z-index: 2; } .code-content .name{ font-size:16px; height: 40px; line-height: 40px; color:#888E9E; background: #F2F3F5; } .text-content{ height:216px; background:#fff; } .code-content .text{ margin-top: 5px; font-size:12px; } .qrcode { position:relative; display: inline-block; width:100px; font-size:12px; color: #888E9E; } .qrcode .code{ margin-top:2px; margin-bottom:10px; padding:5px 0; color:#111217; font-size:13px; font-weight: bold; text-align: center; border:1px solid #ddd; border-radius: 20px; } .qrcode img{ width: 100%; border: 1px solid #ddd; } .qrcode::before{ position:absolute; top:24px; left:0; right:0; display:block; content: ''; border-bottom:1px solid #BABDC5; } .qrcode span{ margin-top:30px; display:block; color:#666; font-size:12px; } .notice{ margin: 0 auto 14px; width: 260px; /* padding-left:20px; */ color:#666; /* background:url('../../assets/images/notice.png') no-repeat left center; */ } .btn-wrap{ margin-top:25px; padding: 16px 0; height: 64px; text-align: center; box-shadow: 0px -1px 5px 0px rgba(92, 102, 116, 0.15); } .btn-wrap button{ width: 102px; height: 32px; font-size: 14px; color: #111217; background: #FFFFFF; border-radius: 3px; border: 1px solid #CED2DA; outline: none; } .btn-wrap button:hover{background:#f5f5f5;} .error{ color:#c8152d; }
3.遇到的问题:
(1)生成图片底部有空白间隙,这个应该是内容高度没控制好,于是给内容高度控制死死的(参数css标红的地方),这样就不会出现空白间隙了(针对固定高度的图片)
(2)图片还是模糊
解决方式有两种:
a.给option设置dpi(从一定程度上可以增加生成图片清晰度)
使用dpi,对插件版本有要求,博主用的插件版本为v1.4.1
b.放大canvas倍数(如果用dpi无效或效果不明显,就放大倍数吧)
function buildCodeImg(data, userName){ if (data.type == "SUCCESS") { var url = 'data:image/png;base64,'+data.data.qrCode; $('#copyCode').attr('data-clipboard-text',data.data.ntCode); $('#copyLink').attr('data-clipboard-text',data.data.link); var canvas = document.createElement("canvas"); var imgDom = document.getElementById('buildImg'); var scale = 4; canvas.width = imgDom.clientWidth * scale; canvas.height = imgDom.clientHeight * scale; canvas.style.width = imgDom.clientWidth * scale + "px"; canvas.style.height = imgDom.clientHeight * scale + "px"; canvas.getContext("2d").scale(scale, scale); //获取context,设置scale var html = '<img src="../../assets/images/cmbnt/recommend-bill.jpg" width="100%">\ <div class="text-content">\ <div class="qrcode">\ <div class="code-title">内推码</div>\ <div class="code">'+data.data.ntCode+'</div>\ <img id="img" src="'+url+'">\ </div>\ <p class="text">微信“扫一扫”参与内部推荐</p>\ </div>\ <div class="name">推荐人:'+userName+'</div>'; $('#buildImg').html(html); $('.js-dialog').show(); html2canvas(document.getElementById('buildImg'), { //dpi: 300, //解决生产图片模糊 scale: scale, width: imgDom.clientWidth, //canvas宽度 height: imgDom.clientHeight,//canvas高度 x: 0, //x坐标 y: 0, //y坐标 backgroundColor: '#888E9E', foreignObjectRendering: true, //是否在浏览器支持的情况下使用ForeignObject渲染 useCORS: true, //是否尝试使用CORS从服务器加载图像 async: false, //是否异步解析和呈现元素 onrendered: function(canvas) { // document.body.appendChild(canvas); var urls = canvas.toDataURL('image/png', 1.0); $('#buildImg').html('<img src="'+urls+'" width="100%"/>') } }); } }
(3)如果高度不固定,甚至会有滚动的情况,需要生成长图,咱要先把页面滚动到顶部。
drawImg() { let dom= document.getElementById("buildImg"); window.pageYOffset = 0 document.documentElement.scrollTop = 0 document.body.scrollTop = 0 html2canvas(dom, { scale: 4, backgroundColor: '#fff', height: dom.offsetHeight, width: dom.offsetWidth, }).then(canvas => { this.canvasUrl = canvas.toDataURL("image/png"); }); },