面试官:请用纯 JS 实现,将 HTML 网页转换为图像

举报
Ocean2022 发表于 2022/05/14 09:24:48 2022/05/14
【摘要】 在工作时,需要实现一个功能:把一个HTML网页的转换为图像。我想到的第一个想法是使用第三方库,但像dom-to-image或使用Chrome Headless,如Puppeteer。那如何使用纯Javascript解决这种需求呢?让我们尝试在不使用任何库的情况下实现这一点。 使用Canvas将HTML网页转换为图像由于安全原因,我们不能直接将HTML绘制到Canvas中。我们将采用另一种更安...

在工作时,需要实现一个功能:把一个HTML网页的转换为图像。我想到的第一个想法是使用第三方库,但像dom-to-image或使用Chrome Headless,如Puppeteer。那如何使用纯Javascript解决这种需求呢?

让我们尝试在不使用任何库的情况下实现这一点。

使用Canvas将HTML网页转换为图像

由于安全原因,我们不能直接将HTML绘制到Canvas中。我们将采用另一种更安全的方法。

  1. 创建包含渲染内容的SVG图像
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
</svg>
  1. 在SVG中插入一个<foreignObject>元素,它将包含HTML
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
    <foreignObject width="100%" height="100%">
    </foreignObject>
</svg>
  1. <foreignObject>节点内添加XHTML内容
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
    <foreignObject width="100%" height="100%">
        <div xmlns="http://www.w3.org/1999/xhtml">. 
          <style>em{color:red;}</style>
             Hey there...
        </div>
    </foreignObject>
</svg>
  1. 创建 img 对象,并将 img 的src 属性设置为该图像的data url:
const tempImg = document.createElement('img')
tempImg.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml">Hey there...</div></foreignObject></svg>')
  1. 将此图像绘制到画布上,并设置画布为img 对象的src属性值:
const newImg = document.createElement('img')
newImg.src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml">Hey there...</div></foreignObject></svg>')

// 对图像添加事件监听
newImg.addEventListener('load', onNewImageLoad)
// 将图像绘制到画布并设置img.src
function onNewImageLoad(e){
  ctx.drawImage(e.target, 0, 0)
  targetImg.src = canvas.toDataURL()
}

完整代码

<html>
<head>
    <meta charset="UTF-8" />
    <style>
    body {
        display: flex;
        flex-flow: column wrap;
        align-items: center;
        justify-content: flex-start;

        background-image: linear-gradient(0, rgb(169, 144, 177), rgb(205, 169, 223));
    }

    h1 {
        margin-top: 5vh;
        margin-bottom: 1vh;

        color: white;
    }

    p {
        margin: 0;
        color: white;
    }
    </style>
</head>

<body>
    <h1>Convert HTML into Image!<h1 />
   <script>
        const { body } = document;
        const canvas = document.createElement("canvas");
        const ctx = canvas.getContext("2d");
        canvas.width = canvas.height = 100;

        const newImg = document.createElement("img");
        newImg.addEventListener("load", onNewImageLoad);
        newImg.src =
            "data:image/svg+xml," +
            encodeURIComponent(
                '<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml"> Hey there...</div></foreignObject></svg>'
            );

        const targetImg = document.createElement("img");
        body.appendChild(targetImg);

        function onNewImageLoad(e) {
            ctx.drawImage(e.target, 0, 0);
            targetImg.src = canvas.toDataURL();
        }
  </script>
</body>

</html>

为什么使用SVG和Canvas是安全的?

SVG图像的实现有很大的限制,因为我们不允许SVG图像加载外部资源,即使是出现在同一个域上的资源。不允许在SVG图像中编写脚本,无法从其他脚本访问SVG图像的DOM, SVG图像中的DOM元素不能接收输入事件。因此,无法将特权信息加载到表单控件中(例如<input type="file">中的完整路径)并呈现它。

从安全性的角度来看,脚本不能直接接触渲染到画布的DOM节点,这一限制非常重要。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。