Top
Fork me on Gitee

js -- html2canvas 海报截图功能

浏览器海报实现

下载 npm i html2canvas or html2canvas.jscanvas2image.js

开发中遇到的一些问题:

  • box-shadow 阴影属性截图后不生效,可以使用阴影图片代替

  • 如果图片是 background 布局,截图出来图片是虚的,只能写 img 布局

  • options 配置中,添加的 scale 参数(可通过下面 getPixelRatio 方法获取设备的像素密度,注意默认不要太小或者固定写 >=2 以防止截图后图片虚)

  • 当要生成的 html 代码中包含 img 标签,并且设置了 object-fit:cover 属性后,通过 html2canvas 生成的图片object-fit:cover 属性没有生效,导致生成的图片与通过样式设置的不一样。需要更改如下源码,当然你需要将此源码单独取出放在 js 中引入到项目中

CanvasRenderer.prototype.renderReplacedElement = function (container, curves, image) {
  // if (image && container.intrinsicWidth > 0 && container.intrinsicHeight > 0) {
  //     var box = contentBox(container);
  //     var path = calculatePaddingBoxPath(curves);
  //     this.path(path);
  //     this.ctx.save();
  //     this.ctx.clip();
  //     this.ctx.drawImage(image, 0, 0, container.intrinsicWidth, container.intrinsicHeight, box.left, box.top, box.width, box.height);
  //     this.ctx.restore();
  // }
  // 上面注释的原来的代码,下面是我们自己修改后的
  // Start Custom Code
  if (image && container.intrinsicWidth > 0 && container.intrinsicHeight > 0) {
      var box = contentBox(container);
      var path = calculatePaddingBoxPath(curves);

      this.path(path);
      this.ctx.save();
      this.ctx.clip();

      let newWidth;
      let newHeight;
      let newX = box.left;
      let newY = box.top;

      if(container.intrinsicWidth / box.width < container.intrinsicHeight / box.height) {
        newWidth = box.width;
        newHeight = container.intrinsicHeight * (box.width / container.intrinsicWidth);
        newY = box.top + (box.height - newHeight) / 2;
      } else {
        newWidth = container.intrinsicWidth * (box.height / container.intrinsicHeight);
        newHeight = box.height;
        newX = box.left + (box.width - newWidth) / 2;
      }

      this.ctx.drawImage(image, 0, 0, container.intrinsicWidth, container.intrinsicHeight, newX, newY, newWidth, newHeight);
      this.ctx.restore();
    }
    // End Custom Code
};
<img :src="`${item.cover}?${new Date().getTime()}`" alt="" class="full-object-cover" crossOrigin="anonymous">

<a v-show="download" :href="imgUri" download="图片名称">下载图片</a>

同步写法

// 获取像素密度
function getPixelRatio(context) {
   var backingStore = context.backingStorePixelRatio ||
      context.webkitBackingStorePixelRatio ||
      context.mozBackingStorePixelRatio ||
      context.msBackingStorePixelRatio ||
      context.oBackingStorePixelRatio ||
      context.backingStorePixelRatio || 1
   if ((window.devicePixelRatio || 1) / backingStore < 1.9) {
      return 2  // 这里最好限制2倍或以上,防止截图后图片太虚
   } else {
      return (window.devicePixelRatio || 1) / backingStore
   }
}
function save_html_as_png(filename = 'image') {
   const eleContent = document.getElementById('overlay-img') // 显示图片的位置
   const posterTemplate = document.getElementsByClassName('poster-template')[0] // 海报模板
   if (eleContent.childNodes.length) return false
   var width = posterTemplate.offsetWidth // 获取(原生)dom 宽度
   var height = posterTemplate.offsetHeight // 获取(原生)dom 高
   // var offsetTop = shareContent.offsetTop;  // 元素距离顶部的偏移量

   var canvas = document.createElement('canvas') // 创建canvas 对象
   var scaleBy = getPixelRatio(context) // 获取像素密度的方法 (也可以采用自定义缩放比例)
   var context = canvas.getContext('2d')
   canvas.width = width * scaleBy // 这里 由于绘制的dom 为固定宽度,居中,所以没有偏移
   // canvas.height = (height + offsetTop) * scaleBy;  // 注意高度问题,由于顶部有个距离所以要加上顶部的距离,解决图像高度偏移问题
   canvas.height = height * scaleBy // 注意高度问题,由于顶部有个距离所以要加上顶部的距离,解决图像高度偏移问题
   context.scale(scaleBy, scaleBy)
   context.translate(0, 0)

  var opts = {
    useCORS: true, // 【重要】开启跨域配置,true 允许加载跨域的图片,默认 false
    scale: scaleBy, // 添加的 scale 参数
    // canvas: canvas, //自定义 canvas
    backgroundColor: null, // 此句可使转出的图没有白边
    width: width, //dom 原始宽度
    height: height,
    logging: false, // 日志开关,便于查看 html2canvas 的内部执行流程
  };
  html2canvas($('body')[0], opts).then(canvas => {
   // 【重要】关闭抗锯齿
   var context = canvas.getContext('2d')
   context.mozImageSmoothingEnabled = false
   context.webkitImageSmoothingEnabled = false
   context.msImageSmoothingEnabled = false
   context.imageSmoothingEnabled = false
   // 调用Canvas2Image插件
   var img = Canvas2Image.convertToImage(canvas, canvas.width, canvas.height);
   // 调用Canvas2Image插件
   //  Canvas2Image.saveAsImage(canvas, canvas.width, canvas.height, 'png', filename);
   
   that.imgUri = image.src // 显示在页面中
   eleContent.appendChild(image)

   that.download = true // 编译过程中不能下载
   posterTemplate.classList.add('hidden') // 隐藏 html 布局
  });
}

异步写法

async function async_save_html_as_png(filename="image") {
  ...
  var canvas = await html2canvas($('body')[0], opts);
  ...
}

需要注意的点:

  1. allowTaint: true 和 useCORS: true 都是解决跨域问题的方式,不同的是使用 allowTaint 会对 canvas 造成污染,导致无法使用 canvas.toDataURL 方法,所以这里不能使用 allowTaint: true;

  2. 在跨域的图片里设置 crossOrigin="anonymous" 并且在给图片 img 标签中 src 属性传图片地址的时候,需要在图片地址后面拼接上一个随机字符串;

  3. canvas.toDataURL('image/jpg') 是将 canvas 转成 base64 图片格式;

  4. 如果是用的阿里云或者其他云平台的 oss 对象存储,还需要在对应的平台上设置一下跨域(这里视情况看是否需要配置)。

其它:

  1. html2canvas 传入的是 dom 对象。这是一个异步函数。可以截图指定元素区域。

  2. Canvas2Image.convertToImage 是同步函数。可以指定图片区域大小。类型可以是 jpeg/png/bmp 等(不区分大小写)。文件名不需要后缀。Canvas2Image.convertToImage 只会下载图片文件。无法存放到指定路径。

QRCode 生成二维码功能

function getQrCode() {
   var url = window.location.href
   QRCode.toCanvas(document.getElementById('code'), url,
      {
         width: 72, // 太小容易识别不灵敏
         height: 72,
         colorDark: '#000000',
         colorLight: '#ffffff',
         typeNumber: 4,
         correctLevel: 'Q', // H
         logging: process.env.NODE_ENV === 'production'? false: true // 日志开关
      },
      error => {
         if (error) console.error(error)
         ...
         save_html_as_png()
      })
},

小程序海报实现wxml-to-canvas

posted @ 2022-08-30 17:58  lisashare  阅读(626)  评论(0编辑  收藏  举报