H5生成海报之html2canvas插件及网络资源图片转base64

5,084 阅读3分钟

html2canvas插件(官网地址):

html2canvas插件相当于把相应的DOM节点里的所有元素按其属性来构建,然后生成截图保存,有些css属性它无法识别(理解不了),所以产生会有点偏差,不过问题不大

浏览器兼容:

  • Firefox 3.5+
  • Google Chrome
  • Opera 12+
  • IE9 +
  • Edge
  • Safari 6+

安装及引入:

npm install html2canvas

# document即是需要绘制的节点,option为一些可配置的选项
import html2canvas from 'html2canvas'
html2canvas(document,option).then(canvas=>{......})
option部分可用参数:
参数名称 类型 默认值 描述
allowTaint boolean false 是否允许跨域
background string #ffffff 画布的背景色,如果没有在DOM中指定,将设置为透明
canvas document null 现有canvas元素用作绘图的基础
imageTimeout number 15000 加载图像的超时时间(单位: 毫秒),设置为0为禁用超时
timeout number 0 图片加载延时,默认延时为0(单位: 毫秒)
logging boolean true 在console.log()输出以进行调试
proxy string undefined 设置跨域代理地址,如果留空则不会加载跨域图像
taintTest boolean true 是否在渲染前测试图片
useCORS boolean false 是否尝试使用CORS从服务器加载图像(也是跨域图像,我使用的时候未成功,原因未知)
width number null 画布宽度
width height null 画布高度

如果要从渲染中排除某些elements,则可以向这些元素添加data-html2canvas-ignore属性,html2cnavas会将它们从渲染中排除

上各种代码:

需要转化成图片的节点统统放在imageWrapper节点里,在节点外的元素不会被一起生成海报,所以上面那个ignore也可以不要,看业务场景吧;之后把整个画布铺满页面,层级设置最高,透明度设置为0, 就可以实现长按保存啦!

//Share.vue --- html
<div class="share">
    <div class="imageWrapper" ref="imageWrapper">
      <img class="real_pic" :src="dataURL" />
      <!-- 需要转化为图片的DOM元素 -->
      <slot>
        <div class="canvas-bg">
          <img class="canvas-title" src="@/images/canvas-title.png" alt />
          <div class="canvas-info">
            <div class="info-content">
              <span>我叫</span>
              <span>{{ userInfo.name }}</span>
            </div>
            <div class="info-content">
              <span>来自</span>
              <span>{{ userInfo.userSchool }}</span>
            </div>
            <div class="info-content">在****大学</div>
            <div class="info-content">
              <span>学习</span>
              <span class="study-day">{{ userInfo.study_day }}</span>
              <span>天啦</span>
            </div>
          </div>
          <div class="canvas-footer">
            <div class="canvas-user">
              <img :src="userInfo.user_image" alt />
            </div>
          </div>
          <div class="canvas-er">
            <img :src="userInfo.share_qrcode" alt />
            <div class="text">长按保存图片</div>
            <div class="text">扫码一起学习</div>
          </div>
        </div>
      </slot>
    </div>
    <div class="watermark">长按海报保存图片</div>
</div>

在mounted的时候调用生成即可,图片加载有时候比较慢,最好在外面加个延时器

//Share.vue --- js
html2canvas(){
    setTimeout(() => {
        html2canvas(this.$refs.imageWrapper, {
            backgroundColor: null, //解决生成会有白边的可能,但是没有遇到有白边,不知道有没有生效
            allowTaint: true, //是否允许跨域图片(官方文档,代试验)
            useCORS: true,
            taintTest: true
          })
        .then(canvas => {
          // this.$refs.imageWrapper.appendChild(canvas)
          let dataURL = canvas.toDataURL("image/png");
          this.dataURL = dataURL;
          this.ready(dataURL);
        })
    }, 1000);
}

问题总结

  1. 一开始使用跨域的图片, 生成的海报有时候没有相应的图片, 所以把图片统一转成了base64格式的, 如果没有跨域问题,不用转换感觉更好
/*
** len -- 图片的宽高,因为我的图片宽高都一样,所以直接一个参数即可
** url -- 图片路径 
*/
getUrlBase64_pro(len,url) {
  //网络资源图片转成base64
  // console.log("图片路径",url)
  var canvas = document.createElement("canvas"); //创建canvas DOM元素
  var ctx = canvas.getContext("2d");
  return new Promise((reslove, reject) => {
    var img = new Image();
    img.crossOrigin = "Anonymous";
    img.onload = function() {
      canvas.height = len; //指定画板的高度,自定义
      canvas.width = len; //指定画板的宽度,自定义
      ctx.drawImage(img, 0, 0, len, len); //参数可自定义
      var dataURL = canvas.toDataURL("image/");
      canvas = null;
      reslove(dataURL);
    };
    img.onerror = function(err){
      reject(err)
    }
    img.src = url;
  });
}

因为有多张图片,我这边直接搞成一个promise,后面就通过Promise.all来同步转换多张图片,之后再生成海报.一次转换,终身受益.

  1. 部分安卓手机生成的海报图片会有一条线,IOS却没有,本来以为是切的图的问题,后来换了图也不行,不知道是兼容性问题还是我的样式有问题.