有没有办法将 html 渲染为像 PNG 这样的图像?我知道使用画布是可能的,但我想渲染标准的 html 元素,例如 div。
将 HTML 渲染为图像
IT技术
javascript
html
css
image
render
2021-02-04 11:39:21
6个回答
有很多选择,它们都有其优点和缺点。
选项 1:使用 API
- ApiFlash(基于chrome)
- EvoPDF(有一个 html 选项)
- 抓斗
- HTML/CSS 到图像 API
- ...
优点
- 执行Javascript
- 近乎完美的渲染
- 正确使用缓存选项时速度很快
- 规模由 API 处理
- 精确计时、视口、...
- 大多数时候他们提供免费计划
缺点
- 如果您打算大量使用它们,则不是免费的
选项 2:使用众多可用库之一
- dom-to-image
- wkhtmltoimage(包含在 wkhtmltopdf 工具中)
- IMGKit(用于 ruby 并基于 wkhtmltoimage)
- imgkit(用于 python 和基于 wkhtmltoimage)
- python-webkit2png
- ...
优点
- 大多数时候转换速度很快
缺点
- 糟糕的渲染
- 不执行javascript
- 不支持最近的 Web 功能(FlexBox、高级选择器、Webfonts、框大小、媒体查询等)
- 有时不是那么容易安装
- 规模复杂
选项 3:使用 PhantomJs 和包装库
- PhantomJs
- node-webshot(PhantomJs 的 JavaScript 包装库)
- ...
优点
- 执行Javascript
- 蛮快
缺点
- 糟糕的渲染
- 不支持最近的 Web 功能(FlexBox、高级选择器、Webfonts、框大小、媒体查询等)
- 规模复杂
- 如果有要加载的图像,让它工作并不那么容易......
选项 4:使用 Chrome Headless 和一个包装库
优点
- 执行Javascript
- 近乎完美的渲染
缺点
- 在以下方面获得完全想要的结果并不那么容易:
- 页面加载时间
- 视口尺寸
- 规模复杂
- 如果 html 包含外部链接,速度会很慢甚至更慢
披露:我是 ApiFlash 的创始人。我已尽力提供诚实而有用的答案。
是的。HTML2Canvas 的存在是为了在<canvas>
其上呈现 HTML (然后您可以将其用作图像)。
注意:有一个已知问题,这不适用于 SVG
我可以推荐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);
});
这里的所有答案都使用第三方库,而在纯 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 范围)并使用画布的结果大小。
我知道这是一个很老的问题,已经有很多答案,但我仍然花了几个小时试图真正做我想做的事:
- 给定一个 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...)