html2canvas 无法渲染网络图片 (JS把图片转换为base64格式)

使用html2canvas插件可以无法渲染图片的情况

在使用html2canvas的时候。如果元素中还包含网络图片。那么有很大的几率渲染不出来。即时把html2canvas的允许跨域打开也无济于事。这次就从根源解决这个问题。而且即时渲染出来了。其实同一张图片已经请求了2次(初始渲染一次,html2canvas渲染也请求了一次)。

问题的根本:在插件中,图片请求的时候跨域了(甚至同域的图片都难请求)

更新,把图片放在OSS(云存储)上,oss允许跨域,图片依然是无法生成
解决:在请求对应oss图片的后面,加一个参数,为时间戳,目的就是不使用oss的缓存,就可以解决跨域问题了,如:
https://test.oss.com/image?t=129094392478


解决思路:

1、在JS中使用 Image 对象。该对象可以设置跨域请求。
2、把图片请求回来后直接渲染到canvas上,注意这里是直接渲染到canvas。并不是通过html2canvas
3、把渲染图片的canvas转成base64的编码格式。
4、把转换好的图片格式重新放到html的元素中
5、使用html2canvas转换对应的html元素


前提条件:

服务器的图片,必须是已经设置了允许跨域的
如果服务器端的图片不允许跨域访问,那么只是前端是很难进行跨域图片处理,以下说的使用 img 标签特性也将无效


代码分步实现:

  • 先准备需要渲染的列表(这时候注意其实对应的image元素不用传入图片的路径了。避免多次渲染)
  <!-- 需要转换的html结构 -->
  <div id="test-box">
    <!-- 这里不需要把图片路径放入src了。直接通过data存储就行 -->
    <img id="img1" src="" data-imgUrl="图片路径.jpg">
    <img id="img2" src="" data-imgUrl="图片路径2.jpg">
  </div>
  // 需要跨域请求的列表
  let imgList = [{
    id: '#img1', // 图片对应渲染的页面节点
    value: $("#img1").data('imgUrl'), // 对应节点的图片(也可以把图片路径直接放这里。因人而异)
  }, {
    id: '#img2',
    value: $("#img2").data('imgUrl'),
  }]

  • 设置 image跨域功能 -> 渲染canvas -> 转换为base64
  • 19/12/14 更新:

出现 OSS已经允许跨域请求,图片依然报错跨域问题
这个是因为OSS可能会存在一个缓存时间的问题,所以就会引发请求偶尔可以,偶尔又跨域的问题,这时候我们就需要在请求图片的时候加上一个时间戳,防止oss图片缓存问题

  /**
   * @description: 图片转base64
   * @param {String} url 需要转换的图片原链接(http://....jpg) 
   * @param {String} outputFormat 转换出来的图片的类型(canvas支持jpg/png格式)
   * @return: 返回图片对应的base64编码
   */
  let getBas64 = (url, outputFormat = 'image/png') => {
    return new Promise(function (resolve, reject) {
      let canvas = document.createElement('CANVAS'),
        ctx = canvas.getContext('2d'),
        img = new Image;

      img.crossOrigin = 'Anonymous'; // 重点!设置image对象可跨域请求
      img.onload = function () {
        canvas.height = img.height;
        canvas.width = img.width;
        ctx.drawImage(img, 0, 0);
        let dataURL = canvas.toDataURL(outputFormat);
        canvas = null;
        resolve(dataURL);
      };
      // img.src = url; // 旧的方式
      img.src = url+'?t='+new Date().valueOf() // 防止oss的缓存问题
    })
  }

  • 最后一步就是拿到 base64 格式的图片。渲染在对应的节点上,渲染完成后用插件转换节点
  html2canvas($('#test-box'), {
    useCORS: true,
    allowTaint: true
  }).then(function (canvas) {
    ImgUrl = canvas.toDataURL(); // 最后拿到了对应节点生成的图片(base64格式)
  })

完整代码示例:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <!-- 需要转换的html结构 -->
  <div id="test-box">
    <!-- 这里不需要把图片路径放入src了。直接通过data存储就行 -->
    <img id="img1" src="" data-imgUrl="图片路径.jpg">
    <img id="img2" src="" data-imgUrl="图片路径2.jpg">
  </div>

  <script>
    // 需要跨域请求的列表
    let imgList = [{
      id: '#img1', // 图片对应渲染的页面节点
      value: $("#img1").data('imgUrl'), // 对应节点的图片(也可以把图片路径直接放这里。因人而异)
    }, {
      id: '#img2',
      value: $("#img2").data('imgUrl'),
    }]

    /**
     * @description: 图片转base64
     * @param {String} url 需要转换的图片原链接(http://....jpg) 
     * @param {String} outputFormat 转换出来的图片的类型(canvas支持jpg/png格式)
     * @return: 返回图片对应的base64编码
     */
    let getBas64 = (url, outputFormat = 'image/png') => {
      return new Promise(function (resolve, reject) {
        let canvas = document.createElement('CANVAS'),
          ctx = canvas.getContext('2d'),
          img = new Image;

        img.crossOrigin = 'Anonymous'; // 重点!设置image对象可跨域请求
        img.onload = function () {
          canvas.height = img.height;
          canvas.width = img.width;
          ctx.drawImage(img, 0, 0);
          let dataURL = canvas.toDataURL(outputFormat);
          canvas = null;
          resolve(dataURL);
        };
        img.src = url;
      })
    }

    // pamoise 初始化请求图片
    Promise.all(imgList.map((item) => getBas64(item.value))).then(function (result) {
      // 返回的图片列表,重新渲染到页面上
      result.forEach((v, k) => {
        $(imgList[k].id).attr('src', v);
      })

      // 赋值完成,生成图片
      html2canvas($('#test-box'), {
        useCORS: true,
        allowTaint: true
      }).then(function (canvas) {
        console.log(canvas) // 生成的canvas对象
        let resImgUrl = canvas.toDataURL(); // 最后拿到了对应节点生成的图片(base64格式)
      })
    })
  </script>
</body>

</html>

  • 需要注意的就是调用的循序。示例代码中是进入页面后直接开始这些步骤了。而且必须先请求图片在使用html2canvas插件
  • 图片的请求也是异步的。这也就是为什么要用上 Promise 方法。而且该方法适用于需要渲染的节点有多张图片的情况。

这些方法都用上后。图片就只需要在Image对象请求的时候图片只需要请求一次。避免多次请求
而且亲测百分百渲染 (解决了 跨域图片/网络图片 无法渲染的问题)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值