近日要开发一个能将生成的二维码另存为图片的功能(该图片呢,肯定不止一个二维码,还包括背景、文字等其他元素),首先呢,就想到了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");
      });
    },

 

 posted on 2019-12-26 17:25  一个大柚子~  阅读(4164)  评论(0编辑  收藏  举报