html2canvas实现将html页面保存为图片

9,004 阅读4分钟

需求背景:

用户通过在微信中分享海报图片邀请新用户注册,海报中有分享者参数的二维码,每个用户的二维码不同 所以海报中有些部分是动态的,不能直接使用图片,需要将html转换为图片。

技术点: html2canvas

html2canvas能够实现在网页或者部分内容直接进行“截屏”,其实现思路是 html2canvas脚本通过读取DOM和应用于元素的不同样式,将页面元素渲染为canvas。它不需要在服务器上进行任何的渲染,它整个图像都是在客户端的浏览器上创建的。因为是基于DOM的,所以它可能不是100%精确到真实的表示,它无法生成实际的屏幕截图,而是基于页面上可用信息构建图片。

兼容性

优缺点:

只需要简单调用html2canvas方法并且设定配置项即可,实现简单。在没有代理的情况下,页面中的图片不能跨域,需要结合属性进行跨域处理;对一些css属性支持不是很友好,不支持flash和iframe标签。

使用方法:

一、配置基本环境

  1. 引入html2canvas npm install --save html2canvas
  2. 把html2canvas引入到需要的组件中 import html2canvas from 'html2canvas'

二、将指定区域的内容转换为图片的方法:

  1. 获取需要转换为图片的内容节点DOM

  2. html转换为canvas: 调用html2canvas函数可以将元素渲染为canvas,该函数返回一个Promise,然后Promise对象会将截取的图片参数传递参数canvas

  3. 调用canvas的toDataUrl方法将DOM输出为包含图片展示的data uri

代码实现:

  1. 首先需要让html2canvas获取到想要转换的节点,需要添加ref标记, 这个节点是要转换内容的父容器,所有的转化内容都要包含在里面。
      <div class="post-con" ref="imgDom">
          ...想要生成图片的html代码
       </div>
  1. 获取目标dom,然后调用 html2canvas 方法生成canvas对象。

    createPicture() {
      this.$nextTick(() => {
        // 获取要转换的元素
        const imgDom: any = this.$refs.imgDom
        // 拿到目标dom调用一下html2canvas方法就能生成canvas对象了
        // 获取要转换的元素
        html2canvas(imgDom, {
          useCORS: true // 开启跨域设置,需要后台设置cors
        }).then((canvas) => {
          // toDataURL函数生成img标签的可用数据  图片格式转成 base64
          let dataURL = canvas.toDataURL("image/png")
          this.imgUrl = dataURL
        })
      })
    }
    
  2. 调用canvasAPI:toDataUrl() 可将 canvas 转为data: uri类型的图片地址,为 base64 格式,再把该图地址赋值给image标签的src属性即可。

<div class="img-con">   <img class="canvas-con" :src="imgUrl"/></div>

踩坑/优化

1、 生成图片清晰度优化

-h tml2canvas在高倍屏下可能会变得模糊,解决这个问题可以考虑 将canvas的属性width和height属性放大为2倍(或者设置为devicePixelRatio倍),最后将canvas样式width和height设置为原先1倍的大小。

createPicture() {
    this.$nextTick(() => {
      // 获取要转换的元素
      const imgDom: any = this.$refs.imgDom
      // 解决转换出来的图片的清晰度问题
      // 手动创建一个 canvas 标签
      const canvas = document.createElement("canvas")
      // 获取父级的宽高
      const width = parseInt(window.getComputedStyle(imgDom).width)
      const height = parseInt(window.getComputedStyle(imgDom).height)
      // 定义放大倍数,可支持小数
      let scale:number = this.getPixelRatio()
      // 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比
      canvas.width = width * scale
      canvas.height = height * scale
      // 设定 canvas css宽高为 DOM 节点宽高
      canvas.style.width = width + 'px'
      canvas.style.height = height + 'px'
      // 获取content,设置scale
      const context = canvas.getContext("2d")
      // context.scale(scale, scale)
      // 拿到目标dom调用一下html2canvas方法就能生成canvas对象了
      // 获取要转换的元素
      html2canvas(imgDom, {
        canvas: canvas,
        scale: scale,
        useCORS: true // 开启跨域设置,需要后台设置cors
      }).then((canvas) => {
        // toDataURL函数生成img标签的可用数据  图片格式转成 base64
        let dataURL = canvas.toDataURL("image/png")
        this.imgUrl = dataURL
      })
    })
  }
  // 动态获取设备的像素比,从而正确放大 canvas
  private getPixelRatio() {
    if (window.devicePixelRatio && window.devicePixelRatio > 1) {
      return window.devicePixelRatio
    }
    return 1
  }
  • 关闭抗锯齿,imageSmoothingEnabled是canvas用来设置图片是否平滑的属性,true表示图片平滑(默认值),false表示关闭canvas抗锯齿。默认情况下,canvas的抗锯齿是开启的,可以通过关闭抗锯齿来实现一定程度上的图片锐化,提高线条边缘的清晰度。

  • 还有一些情况,导出图片模糊是因为原html页面重的图片是以css中background的方式设置的,因为background-size并不会反馈一个具体的宽高数值,导致生成的图片相对模糊,优先使用img标签展示图片。对于必须使用背景图background的元素,可以采用设置待优化元素宽高设置为 2 倍或devicePixelRatio倍,然后通过 css 缩放的方式控制其展示大小不变。

.img-con {
   background: url(/path/to/image) no-repeat;
   width: 100px;
   height: 100px;
   transform: scale(0.5);
   transform-origin: 0 0;
}
  • 为了给到html2canvas明确的整数计算值,避免因小数舍入而导致的拉伸模糊,建议将布局中使用%、vw、vh或者给rem等单位的元素样式,使用px来代替。
  1. html2canvas存在跨域资源无法加载问题,可以配置useCORS:true属性,服务端设置cors,即可实现canvas图片的转化和下载。