是否有一种方法将html渲染成PNG那样的图像?我知道这是可能的与画布,但我想渲染标准的html元素,如div为例。
当前回答
我知道这是一个已经有很多答案的老问题,但我仍然花了很多时间去做我想做的事情:
给定一个HTML文件,从命令行生成一个带有透明背景的(png)图像
使用Chrome headless (version 74.0.3729.157),实际上很容易:
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --headless --screenshot --window-size=256,256 --default-background-color=0 button.html
命令使用说明:
you run Chrome from the command line (here shown for the Mac, but assuming similar on Windows or Linux) --headless runs Chrome without opening it and exits after the command completes --screenshot will capture a screenshot (note that it generates a file called screenshot.png in the folder where the command is run) --window-size allow to only capture a portion of the screen (format is --window-size=width,height) --default-background-color=0 is the magic trick that tells Chrome to use a transparent background, not the default white color finally you provide the html file (as a url either local or remote...)
其他回答
你可以添加引用HtmlRenderer到你的项目,并执行以下操作:
string htmlCode ="<p>This is a sample html.</p>";
Image image = HtmlRender.RenderToImage(htmlCode ,new Size(500,300));
我读了Sjeiti的答案,我觉得很有趣,在那里你只需要几行简单的JavaScript就可以在图像中呈现HTML。
当然,我们必须意识到这种方法的局限性(请在他的回答中阅读其中的一些)。
在这里,我进一步使用了他的代码。
svg图像原则上具有无限分辨率,因为它是矢量图形。但您可能已经注意到,Sjeiti的代码生成的图像分辨率并不高。在将SVG-image转移到canvas-element之前,可以通过缩放SVG-image来解决这个问题,我在下面给出的两个(可运行的)示例代码中的最后一个代码中已经做到了这一点。我在该代码中实现的另一件事是最后一步,即将其保存为png文件。只是为了完成整个过程。
因此,我给出了两个可运行的代码片段:
第一个演示了SVG的无限分辨率。运行它并使用浏览器放大,以查看分辨率是否随着放大而减小。
在您可以运行的代码片段中,我已经使用反勾号指定了一个带有换行符的所谓模板字符串,以便您可以更清楚地看到呈现的HTML。但是,如果HTML在一行之内,那么代码就会非常短,就像这样。
const body = document.getElementsByTagName('BODY')[0];
const img = document.createElement('img')
img.src = 'data:image/svg+xml,' + encodeURIComponent(`<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="border:1px solid red;padding:20px;"><style>em {color:red;}.test {color:blue;}</style>What you see here is only an image, nothing else.<br /><br /><em>I</em> really like <span class="test">cheese.</span><br /><br />Zoom in to check the resolution!</div></foreignObject></svg>`);
body.appendChild(img);
这里是一个可运行的代码片段。
const body = document.getElementsByTagName('BODY')[0]; const img = document.createElement('img') img.src = 'data:image/svg+xml,' + encodeURIComponent(` <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="border:1px solid red;padding:20px;"> <style> em { color:red; } .test { color:blue; } </style> What you see here is only an image, nothing else.<br /> <br /> <em>I</em> really like <span class="test">cheese.</span><br /> <br /> Zoom in to check the resolution! </div> </foreignObject> </svg> `); body.appendChild(img);
放大并检查SVG的无限分辨率。
下面的下一个可运行文件实现了我上面提到的两个额外步骤,即通过缩放SVG来提高分辨率,然后将其保存为png图像。
window.addEventListener("load", doit, false) var canvas; var ctx; var tempImg; function doit() { const body = document.getElementsByTagName('BODY')[0]; const scale = document.getElementById('scale').value; let trans = document.getElementById('trans').checked; if (trans) { trans = ''; } else { trans = 'background-color:white;'; } let source = ` <div xmlns="http://www.w3.org/1999/xhtml" style="border:1px solid red;padding:20px;${trans}"> <style> em { color:red; } .test { color:blue; } </style> What you see here is only an image, nothing else.<br /> <br /> <em>I</em> really like <span class="test">cheese.</span><br /> <br /> <div style="text-align:center;"> Scaling: <br /> ${scale} times! </div> </div>` document.getElementById('source').innerHTML = source; canvas = document.createElement('canvas'); ctx = canvas.getContext('2d'); canvas.width = 200*scale; canvas.height = 200*scale; tempImg = document.createElement('img'); tempImg.src = 'data:image/svg+xml,' + encodeURIComponent(` <svg xmlns="http://www.w3.org/2000/svg" width="${200*scale}" height="${200*scale}"> <foreignObject style=" width:200px; height:200px; transform:scale(${scale}); " >` + source + ` </foreignObject> </svg> `); } function saveAsPng(){ ctx.drawImage(tempImg, 0, 0); var a = document.createElement('a'); a.href = canvas.toDataURL('image/png'); a.download = 'image.png'; a.click(); } <table border="0"> <tr> <td colspan="2"> The claims in the HTML-text is only true for the image created when you click the button. </td> </tr> <tr> <td width="250"> <div id="source" style="width:200px;height:200px;"> </div> </td> <td valign="top"> <div> In this example the PNG-image will be squarish even if the HTML here on the left is not exactly squarish. That can be fixed.<br> To increase the resolution of the image you can change the scaling with this slider. <div style="text-align:right;margin:5px 0px;"> <label style="background-color:#FDD;border:1px solid #F77;padding:0px 10px;"><input id="trans" type="checkbox" onchange="doit();" /> Make it transparent</label> </div> <span style="white-space:nowrap;">1<input id="scale" type="range" min="1" max="10" step="0.25" value="2" oninput="doit();" style="width:150px;vertical-align:-8px;" />10 <button onclick="saveAsPng();">Save as PNG-image</button></span> </div> </td> </tr> </table>
尝试不同的比例。例如,如果您将缩放设置为10,那么您将在生成的png图像中获得非常好的分辨率。我还添加了一个额外的功能:一个复选框,这样你就可以使png图像透明。
注意:
当这个脚本在Stack Overflow运行时,保存按钮在Chrome和Edge中不工作。原因是https://www.chromestatus.com/feature/5706745674465280。
因此,我也把这个片段放在https://jsfiddle.net/7gozdq5v/上,它适用于这些浏览器。
这里所有的答案都使用第三方库,而在纯Javascript中将HTML渲染为图像可以相对简单。在MDN的画布部分甚至有一篇关于它的文章。
诀窍是这样的:
使用包含XHTML的foreignObject节点创建SVG 将图像的src设置为该SVG的数据url 将图像绘制到画布上 将画布数据设置为目标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() }
需要注意的一些事情
The HTML inside the SVG has to be XHTML For security reasons the SVG as data url of an image acts as an isolated CSS scope for the HTML since no external sources can be loaded. So a Google font for instance has to be inlined using a tool like this one. Even when the HTML inside the SVG exceeds the size of the image it wil draw onto the canvas correctly. But the actual height cannot be measured from that image. A fixed height solution will work just fine but dynamic height will require a bit more work. The best is to render the SVG data into an iframe (for isolated CSS scope) and use the resulting size for the canvas.
有很多选择,它们都有各自的优点和缺点。
选项1:使用API
ApiFlash(基于chrome) EvoPDF(有html选项) Grabzit HTML/CSS图像API ...
Pros
执行Javascript 近乎完美渲染 正确使用缓存选项时快速 缩放由api处理 精确计时,视口,… 大多数时候,他们提供的是免费方案
Cons
如果你打算经常使用它们,就不是免费的
选项2:使用众多可用库中的一个
dom-to-image Wkhtmltoimage(包含在wkhtmltopdf工具中) IMGKit(用于ruby和基于wkhtmltoimage) Imgkit(适用于python,基于wkhtmltoimage) python-webkit2png ...
Pros
大多数时候,转换是相当快的
Cons
坏的呈现 不执行javascript 不支持最近的网页功能(FlexBox,高级选择器,网页字体,盒子大小,媒体查询,…) 有时不容易安装 缩放复杂
选项3:使用PhantomJs和包装器库
PhantomJs node-webshot(用于PhantomJs的javascript包装库) ...
Pros
执行Javascript 很快
Cons
坏的呈现 不支持最近的网页功能(FlexBox,高级选择器,网页字体,盒子大小,媒体查询,…) 缩放复杂 不那么容易使它工作,如果有图像要加载…
选项4:使用Chrome Headless和一个包装器库
Chrome无头 chrome-devtools-protocol Puppeteer (Chrome无头浏览器的javascript包装库) ...
Pros
执行Javascript 近乎完美渲染
Cons
要得到想要的结果并不容易: 页面加载时间 视窗尺寸 缩放复杂 相当慢,甚至更慢,如果html包含外部链接
披露:我是ApiFlash的创始人。我尽力提供了一个诚实而有用的答案。
你可以使用PhantomJS,这是一个无头webkit (safari和chrome(直到最近)的渲染引擎)驱动程序。您可以在这里学习如何对页面进行屏幕捕获。希望有帮助!
推荐文章
- 给一个数字加上st, nd, rd和th(序数)后缀
- 如何以编程方式触发引导模式?
- setTimeout带引号和不带括号的区别
- 为什么我的CSS3媒体查询不能在移动设备上工作?
- 在JS的Chrome CPU配置文件中,'self'和'total'之间的差异
- 用javascript检查输入字符串中是否包含数字
- 如何使用JavaScript分割逗号分隔字符串?
- 在Javascript中~~(“双波浪号”)做什么?
- 谷歌chrome扩展::console.log()从后台页面?
- 下一个元素的CSS选择器语法是什么?
- 未捕获的SyntaxError:
- 是否有'box-shadow-color'属性?
- [].slice的解释。调用javascript?
- 在jQuery中的CSS类更改上触发事件
- jQuery日期/时间选择器