Dom转图片

前言

最近在项目中遇到一个需求,把活动页面转换为海报图片分享出去,在做这个需求时,尝试了以下两种方案,基于此做以下记录和分享。

方法一 html2canvas

使用方法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #poster{
            width: 700px; 
            height: 500px;
            background-color: green;
        }
    </style>
    <script type="text/javascript" src="http://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>
    <script type="text/javascript">
        function takeScreenshot() {
            const node = document.getElementById('poster')
            html2canvas(node, {
                useCORS: true,
                height: node.offsetHeight,
                width: node.offsetWidth,
                scrollY: 0,
                scrollX: 0
            }).then(async (canvas) => {
                let oImg = new Image();
                oImg.src = canvas.toDataURL();  // 导出图片
                document.body.appendChild(oImg);  // 将生成的图片添加到body
            })
        }
    </script>
</head>
<body>
    <div id="poster">
        <input type="button" value="截图" onclick="takeScreenshot()">
    </div>
</body>
</html>
复制代码

注意事项:

  1. 生成的图片模糊

有时候我们会发现,导出的图片局部有些图片看起来没有原图那么清晰,这其实是因为你使用背景图片的原因。解决方法也很简单,就是直接使用标签就好了

  1. 生成出来的图片有白色边框

在配置项中设置backgroundColor: null即可

  1. 生成图片不显示

这是最常见的一个bug,这基本就是因为图片素材出现跨域,基本就是在方法上调用时增加两个配置

allowTaint: true,
useCORS: true 
复制代码

还有一个方法就是,把跨域的图片转为base64,也可以完美解决这个问题。

  1. PNG图片不透明

有时你可能用到透明的PNG图片作为背景图,可是结果最后生成的图片却并不透明,这是因为html2canvas生成的canvas背景颜色默认为白色的缘故,所以导出的图片背景颜色当然也是白色。 解决方案也是添加一个配置项就好(事实上经实验发现只要是非颜色类型的字符串都可以)。

backgroundColor: 'transparent'
复制代码
  1. 生成的图片加载闪动效果

先让生成的图片隐藏,添加图片加载状态,等图片生成好以后再展示。

GitHub

官方文档

方法二 dom-to-image

主要使用方法:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #poster{
            width: 700px; 
            height: 500px;
            background-color: green;
        }
    </style>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dom-to-image/2.6.0/dom-to-image.min.js"></script>
    <script type="text/javascript">
        function takeScreenshot() {
            const node = document.getElementById('poster')
            domtoimage.toPng(node)
            .then((dataUrl) => {
                var img = new Image();
                img.src = dataUrl;
                document.body.appendChild(img);
            })
            .catch((error) => {
                console.error('oops, something went wrong!', error);
            });
        }
    </script>
</head>
<body>
    <div id="poster">
        <input type="button" value="截图" onclick="takeScreenshot()">
    </div>
</body>
</html>
复制代码

1. domtoimage.toPng(…);将节点转化为png格式的图片。

  function takeScreenshot() {
      const node = document.getElementById('poster')
      domtoimage.toPng(node)
      .then((dataUrl) => {
          var img = new Image();
          img.src = dataUrl;
          document.body.appendChild(img);
      })
      .catch((error) => {
          console.error('oops, something went wrong!', error);
      });
  }
复制代码

2. domtoimage.toJpeg(…);将节点转化为jpg格式的图片。

  function takeScreenshot() {
      const node = document.getElementById('poster')
      domtoimage.toJpeg(node, { quality: 0.95 })
      .then((dataUrl) => {
          var img = new Image();
          img.src = dataUrl;
          document.body.appendChild(img);
      })
      .catch((error) => {
          console.error('oops, something went wrong!', error);
      });
  }
复制代码

3. domtoimage.toSvg(…);将节点转化为svg格式的图片,生成的图片的格式都是base64格式。

  function filter (node) {
      return (node.tagName !== 'i');
  }
  function takeScreenshot() {
      const node = document.getElementById('poster')
      domtoimage.toSvg(node, { filter: filter })
      .then((dataUrl) => {
          var img = new Image();
          img.src = dataUrl;
          document.body.appendChild(img);
      })
      .catch((error) => {
          console.error('oops, something went wrong!', error);
      });
  }
复制代码

4. domtoimage.toBlob(…);将节点转化为二进制格式,这个可以直接将图片下载。

  function takeScreenshot() {
      const node = document.getElementById('poster')
      domtoimage.toBlob(node)
      .then((blob) => {
      	window.saveAs(blob, 'poster.png');
      })
      .catch((error) => {
          console.error('oops, something went wrong!', error);
      });
  }
复制代码

5. domtoimage.toPixelData(…);获取原始像素值,以Uint8Array 数组的形式返回,每4个数组元素表示一个像素点,即rgba值。这个方法也是挺实用的,可以用于WebGL中编写着色器颜色。

  function takeScreenshot() {
      const node = document.getElementById('poster')
      domtoimage.toPixelData(node)
      .then((pixels) => {
      	for (var y = 0; y < node.scrollHeight; ++y) {
          for (var x = 0; x < node.scrollWidth; ++x) {
            pixelAtXYOffset = (4 * y * node.scrollHeight) + (4 * x);
            /* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
            pixelAtXY = pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
          }
        }
      })
      .catch((error) => {
          console.error('oops, something went wrong!', error);
      });
  }
复制代码

注意事项:

  1. 用于生成图片的dom元素不能display:none或opacity:0。可以用z-index放到其它元素下面;或者用绝对定位,将其放到某个div容器的可视区之外,然后容器设置overflow:hidden
  2. 用于生成图片的dom元素的父元素们不能display:none
  3. 综合1、2,即用于生成图片的dom元素要本身是可见的,且能计算到尺寸值,但不需要出现在视野中
  4. 页面不能出现隐藏iframe的样式,例如iframe{display:none}
  5. 生成后的图,会自动追加到用于生成的dom元素后面,类名是dom2img-result,如果不想把图片的样子展示给用户,可将其样式设置为opacity:0

GitHub

分类:
阅读
标签: