基于html2canvas实现生成图片海报

2022年8月21日 292点热度 0人点赞 0条评论

iScott推荐搜索

将整个网页保存为图片是一个十分有趣的功能,常见于H5活动页的结尾页分享。以下则是项目中的一些小结和汇总。

实现html页面保存为图片




现有已知能够实现网页保存为图片的方案包括:

  • 方案1:将DOM改写为canvas,然后利用canvas的toDataURL方法实现将DOM输出为包含图片展示的data URI

  • 方案2:使用html2canvas.js实现(可选搭配Canvas2Image.js实现网页保存为图片)

  • 方案3:使用rasterizeHTML.js实现

解决方案的选择

  • 方案1:需要手动计算每个DOM元素的Computed Style,然后需要计算好元素在canvas的大小位置等属性。

方案一的难点:

  1. 相当于完全重写了整个页面的布局样式,增加了工作量。

  2. 由于canvas中没有的对象概念,对于元素丰富、布局复杂的页面,不易重

    构。

  3. 所有DOM元素改写进canvas会带来一些困难,例如:难以支持响应式,图片元素清晰度不佳和文字点击区域识别问题等。

  • 方案2:该类功能中Github上stars最多(至今仍在维护),Stack Overflow亦有丰富的讨论。只需简单调用html2canvas方法并设定配置项即可。

  • 方案3:该方案的限制较多,目前仅支持3类可转为canvas的目标格式

    : 页面url,html字符串和document对象。

小结html2canvas是目前实现网页保存为图片功能的综合最佳选择。

html2canvas的使用方法


我的需求是,将项目的网址生成一个二维码,然后把二维码放到生成的海报中间。


// 生成二维码// 使用qrcode.js,使用方法参考官方文档// http://code.ciaoca.com/javascript/qrcode/getQrCode:function(){  let codeEle = document.getElementById('code');  // dom元素  var typeNumber = 4;  var errorCorrectionLevel = 'L';  var qr = qrcode(typeNumber, errorCorrectionLevel);  qr.addData('http://resume.ethan.pub');  qr.make();  codeEle.innerHTML = qr.createImgTag();}

html转canvas


基于html2canvas.js可将一个元素渲染为canvas,只需要简单的调用html2canvas(element[, options]);即可。下列html2canvas方法会返回一个包含有<canvas>元素的promise


html2canvas(document.body).then(function(canvas) {    document.body.appendChild(canvas);});

canvas转image


上一步生成的canvas即为包含目标元素的<canvas>元素对象。实现保存图片的目标只需要将canvas转image即可。

这里的转换方案有2种

  • 方案1:基于原生canvas的toDataURL方法将canvas输出为data: URI类型的图片地址,再将

    该图片地址赋值给<image>元素的src属性即可

  • 方案2:使用第三方库Canvas2Image.js,调用其convertToImage方法即可

实际上,Canvas2Image.js也是基于canvas.toDataURL的封装,相比原生的canvas API对于转为图片的功能上考虑更为具体(未压缩的包大小为7.4KB),适合项目使用。

清晰度优化

生成图片之后会发现图片稍微有些模糊,最终图片的清晰度取决于第一步中html转换成的canvas的清晰度。

现有解决方案参考:

  • html5 canvas在高倍屏下变模糊的处理办法

  • html5 canvas绘制图片模糊的问题

其基本原理是canvas的属性widthheight属性放大为2倍(或者设置为devicePixelRatio倍),最后将canvas的CSS样式width和height设置为原先1倍的大小。

含有跨域图片的配置

由于canvas对于图片资源的同源限制,如果画布中包含跨域的图片资源则会污染画布,造成生成图片样式混乱或者html2canvas方法不执行等问题。

解决方案:

  • 要求CDN的图片配置好CORS

    CDN配置好后,通过chrome开发者工具可以看到响应头中应含有Access-Control-Allow-Origin的字段。

  • 开启html2canvasuseCORS配置项如下设置:

var opts = {useCORS: true};html2canvas(element, opts);

完整demo代码

<div id="share">  <div class="content">    <div class="card-top">      <div class="text-wrap">        <p>Ethan — 前端工程师</p>      </div>    </div>  <div class="card-bottom">    <div class="info">洛阳亲故如相问,就说我在敲代码。</div>      <div class="code-con">        <div id="code"></div>        <div class="code-me">扫描二维码访问更多内容</div>      </div>    </div>  </div></div>
#share{  position: absolute;  z-index: -1;  .content{    width: 300px;    height: auto;    background: #fff;    margin: 0;    padding: 0;  }  .card-top{    width: 100%;    height: 210px;    background: url(http://ethanwp.oss-cn-shenzhen.aliyuncs.com/wp/2018/12/wBHYXkfkJ_E.jpg) center no-repeat;    background-size: 100% 100%;    z-index: 1;    display: flex;    align-items: flex-end;    .text-wrap{      width: 100%;      height: 30px;      background: linear-gradient(360deg, rgba(0,0,0,0.3),rgba(0,0,0,0));      p{        width:100%;        height: 30px;        text-align: center;        color: #fff;        font-size: 16px;      }    }  }  .card-bottom{    width: 100%;    .info{       width: 100%;       height: 20px;       line-height: 20px;       text-align: center;       margin-top: 10px;       color: #8692a4;    }    .code-con{      height: auto;      padding-bottom: 10px;      text-align: center;      #code{        width: 100%;        text-align: center;        height: 100px;        img{          width: 100px;          height: 100px;        }      }      .code-me{        width: 100%;        text-align: center;        color: #8692a4;      }    }  }}
handleShare(){  const _self = this;  //定义查找元素方法  function $(selector) {    return document.querySelector(selector);  }  var main = {    init:function(){      _self.show = true;      main.getQrCode();  },  //设置监听事件  getQrCode:function(){    let codeEle = document.getElementById('code');    var typeNumber = 4;    var errorCorrectionLevel = 'L';    var qr = qrcode(typeNumber, errorCorrectionLevel);    qr.addData('http://resume.ethan.pub');    qr.make();    codeEle.innerHTML = qr.createImgTag();    main.html2Canvas();  },  //获取像素密度  getPixelRatio:function(context){    var backingStore = context.backingStorePixelRatio ||    context.webkitBackingStorePixelRatio ||    context.mozBackingStorePixelRatio ||    context.msBackingStorePixelRatio ||    context.oBackingStorePixelRatio ||    context.backingStorePixelRatio || 1;    return (window.devicePixelRatio || 1) / backingStore;  },                  //绘制dom 元素,生成截图canvas  html2Canvas: function () {    var shareContent = $("#share");// 需要绘制的部分的 (原生)dom 对象 ,注意容器的宽度不要使用百分比,使用固定宽度,避免缩放问题    var width = shareContent.offsetWidth;  // 获取(原生)dom 宽度    var height = shareContent.offsetHeight; // 获取(原生)dom 高    var offsetTop = shareContent.offsetTop;  //元素距离顶部的偏移量    var canvas = document.createElement('canvas');  //创建canvas 对象    var context = canvas.getContext('2d');    var scaleBy = main.getPixelRatio(context);  //获取像素密度的方法 (也可以采用自定义缩放比例)    canvas.width = width * scaleBy;   //这里 由于绘制的dom 为固定宽度,居中,所以没有偏移    canvas.height = (height + offsetTop) * scaleBy;  // 注意高度问题,由于顶部有个距离所以要加上顶部的距离,解决图像高度偏移问题    context.scale(scaleBy, scaleBy);    context.translate(00);    var opts = {      useCORS:true,//允许加载跨域的图片      scale:scaleBy, // 添加的scale 参数      canvas:canvas, //自定义 canvas      width:width, //dom 原始宽度      height:height //dom 原始高度    };    html2canvas(shareContent, opts).then(function (canvas) {      var context = canvas.getContext('2d');      // 【重要】关闭抗锯齿      context.mozImageSmoothingEnabled = false;      context.webkitImageSmoothingEnabled = false;      context.msImageSmoothingEnabled = false;      context.imageSmoothingEnabled = false;      _self.canvas = canvas;      var img = Canvas2Image.convertToImage(canvas, canvas.width, canvas.height);      img.style.width = canvas.width / scaleBy + "px";      img.style.height = canvas.height / scaleBy + "px";      document.getElementById('content').appendChild(img);      if (_self.percent > 0) {        _self.percent = 0      }      let sid = window.setInterval(() => {        _self.percent++        if (_self.percent >= 100) {          _self.hasShare = true;          window.clearInterval(sid)        }      }, 10)      $('#share').style.display = 'none'    });   }};  //最后运行代码  if(this.isTap){    return;    this.isTap = false;  } else {    this.isTap = true;    if(this.hasShare){      this.show = true    } else {      main.init();    }  }            },

效果图

图片

84290基于html2canvas实现生成图片海报

这个人很懒,什么都没留下

文章评论