将 HTML 渲染为图像

IT技术 javascript html css image render
2021-02-04 11:39:21

有没有办法将 html 渲染为像 PNG 这样的图像?我知道使用画布是可能的,但我想渲染标准的 html 元素,例如 div。

6个回答

有很多选择,它们都有其优点和缺点。

选项 1:使用 API

优点

  • 执行Javascript
  • 近乎完美的渲染
  • 正确使用缓存选项时速度很快
  • 规模由 API 处理
  • 精确计时、视口、...
  • 大多数时候他们提供免费计划

缺点

  • 如果您打算大量使用它们,则不是免费的

选项 2:使用众多可用库之一

优点

  • 大多数时候转换速度很快

缺点

  • 糟糕的渲染
  • 不执行javascript
  • 不支持最近的 Web 功能(FlexBox、高级选择器、Webfonts、框大小、媒体查询等)
  • 有时不是那么容易安装
  • 规模复杂

选项 3:使用 PhantomJs 和包装库

优点

  • 执行Javascript
  • 蛮快

缺点

  • 糟糕的渲染
  • 不支持最近的 Web 功能(FlexBox、高级选择器、Webfonts、框大小、媒体查询等)
  • 规模复杂
  • 如果有要加载的图像,让它工作并不那么容易......

选项 4:使用 Chrome Headless 和一个包装库

优点

  • 执行Javascript
  • 近乎完美的渲染

缺点

  • 在以下方面获得完全想要的结果并不那么容易:
    • 页面加载时间
    • 视口尺寸
  • 规模复杂
  • 如果 html 包含外部链接,速度会很慢甚至更慢

披露:我是 ApiFlash 的创始人。我已尽力提供诚实而有用的答案。

PhantomJS 支持动态的东西,比如谷歌地图。它真的是一个完整的网络浏览器,只是没有附加显示器。但是,您必须稍等片刻,让 Google Map 加载具有地理信息的图块(我等待 500 毫秒并允许人们更改延迟 - 如果这还不够,在不增加时间的情况下再次运行就可以了,因为这些图块已被缓存)。
2021-03-12 11:39:21
wkhtmltoimage/pdf 确实支持 javascript 渲染。您可以设置 javascript 延迟或让 wkhtml 检查特定的 window.status (当您知道您的 js 内容已完成时,您可以使用 javascript 进行设置)
2021-04-07 11:39:21

是的。HTML2Canvas 的存在是为了在<canvas>上呈现 HTML (然后您可以将其用作图像)。

注意:有一个已知问题,这不适用于 SVG

另一个问题,如果元素隐藏或隐藏在另一个元素后面,这将不起作用
2021-03-19 11:39:21
看起来很有趣,但我没能成功,所以我选择了 John Fisher 解决方案。谢谢你的信息,我以后会看这个项目的!
2021-03-20 11:39:21
这个解决方案很慢
2021-03-25 11:39:21
dom-to-image(请参阅 tsayen 的回答)在渲染准确图片方面做得更好。
2021-03-29 11:39:21
如果您完全处理 SVG,则 dom-to-image 效果会更好。
2021-04-08 11:39:21

我可以推荐dom-to-image库,它是专门为解决这个问题而编写的(我是维护者)。
这是你如何使用它(这里还有一些):

var node = document.getElementById('my-node');

domtoimage.toPng(node)
    .then (function (dataUrl) {
        var img = new Image();
        img.src = dataUrl;
        document.appendChild(img);
    })
    .catch(function (error) {
        console.error('oops, something went wrong!', error);
    });
我想到的第一件事 - 尝试将您的图像渲染为 SVG(使用domtoimage.toSvg),然后自己在画布上渲染它并尝试以某种方式使用该画布的分辨率。可能可以在库本身中实现这种功能作为某种渲染选项,因此您可以以像素为单位传递图像尺寸。如果你需要它,我很感激你在 github 上创建一个问题。
2021-03-17 11:39:21
@Subho 这是一个包含带有 base64 编码数据的 URL 的字符串
2021-03-18 11:39:21
这很棒!有什么方法可以使输出大小加倍?
2021-04-08 11:39:21
这比 html2canvas 好得多。谢谢。
2021-04-08 11:39:21
是否可以使用这个库,节点 js 服务器端本身在浏览器之前生成图像?
2021-04-09 11:39:21

这里的所有答案都使用第三方库,而在纯 Javascript 中将 HTML 呈现为图像可能相对简单。还有甚至一个关于它的文章上MDN画布部分。

诀窍是这样的:

  • 使用包含 XHTML 的 foreignObject 节点创建一个 SVG
  • 将图像的 src 设置为该 SVG 的数据 url
  • drawImage 在画布上
  • 将画布数据设置为目标 image.src

const {body} = document

const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = canvas.height = 100

const tempImg = document.createElement('img')
tempImg.addEventListener('load', onTempImageLoad)
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"><style>em{color:red;}</style><em>I</em> lick <span>cheese</span></div></foreignObject></svg>')

const targetImg = document.createElement('img')
body.appendChild(targetImg)

function onTempImageLoad(e){
  ctx.drawImage(e.target, 0, 0)
  targetImg.src = canvas.toDataURL()
}

一些注意事项

  • SVG 内的 HTML 必须是 XHTML
  • 出于安全原因,SVG 作为图像的数据 url 充当 HTML 的独立 CSS 范围,因为无法加载外部源。因此,谷歌的字体,例如有使用类似工具内联这一个
  • 即使 SVG 中的 HTML 超过图像的大小,它也会正确地绘制到画布上。但是无法从该图像中测量实际高度。固定高度的解决方案可以很好地工作,但动态高度需要更多的工作。最好的方法是将 SVG 数据渲染到 iframe(用于隔离的 CSS 范围)并使用画布的结果大小。
- 稍后进行一些快速而肮脏的测试 - jsfiddle.net/Sjeiti/pcwudrmc/75965 Chrome、Edge 和 Firefox 的宽度高度至少为 10,000 像素,(在本例中)是大约 10,000,000 的字符数和 png 文件大小8MB。尚未经过严格测试,但对于大多数用例来说已经足够了。
2021-03-20 11:39:21
那篇文章已经没有了。
2021-03-23 11:39:21
这个答案stackoverflow.com/questions/12637395/...似乎表明你可以走得很远。Mozilla 和 Chrome 具有无限的数据 uri 长度,甚至当前的 IE 也允许 4GB,归结为大约 3GB 的图像(由于 base64)。但这将是一个很好的测试。
2021-03-25 11:39:21
很好的答案,但要评论 HTML 和 Web API 的最新技术,像往常一样,这看起来像是把某些人拉到了后面——所有功能在技术上都存在,但暴露在一个奇怪的代码路径数组(没有双关语)后面,类似于设计良好的 API 不会像您期望的那样清晰。简单地说:浏览器允许将 aDocument渲染成光栅(例如 a Canvas很可能是微不足道的,但是因为没有人费心对其进行标准化,我们不得不像上面说明的那样疯狂(无意冒犯作者代码)。
2021-03-31 11:39:21
好的!删除外部库依赖确实是一个好方法。我改变了我接受的答案:-)
2021-04-07 11:39:21

我知道这是一个很老的问题,已经有很多答案,但我仍然花了几个小时试图真正做我想做的事:

  • 给定一个 html 文件,从命令行生成一个具有透明背景的 (png) 图像

使用 Chrome headless(截至本回复的版本为 74.0.3729.157),实际上很容易:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless --screenshot --window-size=256,256 --default-background-color=0 button.html

命令的解释:

  • 你从命令行运行 Chrome(这里显示的是 Mac,但假设在 Windows 或 Linux 上类似)
  • --headless 运行 Chrome 而不打开它并在命令完成后退出
  • --screenshot将捕获屏幕截图(请注意,它会生成一个screenshot.png在运行命令的文件夹中调用的文件)
  • --window-size只允许捕获屏幕的一部分(格式为--window-size=width,height
  • --default-background-color=0是告诉 Chrome 使用透明背景的魔术,而不是默认的白色
  • 最后你提供 html 文件(作为本地或远程的 url...)
--screenshot='absolute\path\of\screenshot.png' 为我工作
2021-03-13 11:39:21
命令行工作。如果我想从 express js 以编程方式运行它,如何运行它?
2021-03-19 11:39:21
svg 对我来说不是 svg ......它只是带有 SVG 扩展名的 PNG ......所以要小心。
2021-03-25 11:39:21
仅供参考,即使它没有在手册页中提到该选项,这也适用于铬。
2021-03-28 11:39:21
非常好!它也适用于 SVG!我最终切换到您的解决方案!;-)
2021-04-03 11:39:21