有没有办法将 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...)