在浏览器中将 SVG 转换为图像(JPEG、PNG 等)
Posted
技术标签:
【中文标题】在浏览器中将 SVG 转换为图像(JPEG、PNG 等)【英文标题】:Convert SVG to image (JPEG, PNG, etc.) in the browser 【发布时间】:2011-04-27 21:24:13 【问题描述】:我想通过 javascript 将 SVG 转换为位图图像(如 JPEG、PNG 等)。
【问题讨论】:
您真正想要完成的任务是什么?尽管 echo-flows 的回答告诉我们,(在某些浏览器中)可能有更好、更简单的转换方法适用于几乎所有实际情况。 这里是一个使用 d3 的例子:***.com/a/23667012/439699 svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - 完美运行! [在链接页面上,sourceSVG = $("#your_svg_elem_name").get(0) ] 相关:***.com/questions/3173048/… mybyways.com/blog/convert-svg-to-png-using-your-browser 【参考方案1】:以下是通过 JavaScript 实现的方法:
-
使用 canvg JavaScript 库通过 Canvas 呈现 SVG 图像:https://github.com/gabelerner/canvg
根据以下说明从画布中捕获编码为 JPG(或 PNG)的数据 URI:Capture html Canvas as gif/jpg/png/pdf?
【讨论】:
这不是严格意义上的 Javascript,而是 HTML5。这不适用于 IE8 或任何其他不支持 HTML5 Canvas 的浏览器。 如果浏览器支持 SVG 和画布,那么将有更简单的方法将 SVG 加载到内存中,然后将其绘制到画布中,而不需要 Canvg,这是一个非常大的库因为它处理支持 SVG 的浏览器已经免费提供的所有 SVG 解析。我不确定这是否满足原始用例,但如果满足,那么 see this resource for details. 感谢您不支持 IE8。人们应该明白,是时候继续前进了。 您现在可以使用 JavaScript SVG 库 Pablo 来实现这一点(我做到了)。请参阅toImage()
和 download()
以获取自动下载的图像。
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - 完美运行! [在链接页面上,sourceSVG = $("#your_svg_elem_name").get(0) ]【参考方案2】:
jbeard4 解决方案效果很好。
我正在使用Raphael SketchPad 创建一个 SVG。链接到步骤 1 中的文件。
对于保存按钮(svg 的 id 是“editor”,canvas 的 id 是“canvas”):
$("#editor_save").click(function()
// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());
// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
);
【讨论】:
canvg 需要第二个参数是<svg>...</svg
但 jquery html() 函数不添加 svg 标签,所以这段代码对我有用,但我需要将 canvg 实时编辑为 canvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
@Luckyn 如果您在 svg 元素的父级上调用 $(selector).html()
,它将起作用
@Luckyn 和@jonathanGB,你不应该在包装器上使用html()
,或者手动构造父svg
标签——它甚至可能有你在这个hack 中遗漏的属性。只需使用 $(svg_elem)[0].outerHTML
即可为您提供 svg 及其内容的完整来源。只是说...【参考方案3】:
这似乎适用于大多数浏览器:
function copyStylesInline(destinationNode, sourceNode)
var containerElements = ["svg","g"];
for (var cd = 0; cd < destinationNode.childNodes.length; cd++)
var child = destinationNode.childNodes[cd];
if (containerElements.indexOf(child.tagName) != -1)
copyStylesInline(child, sourceNode.childNodes[cd]);
continue;
var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
if (style == "undefined" || style == null) continue;
for (var st = 0; st < style.length; st++)
child.style.setProperty(style[st], style.getPropertyValue(style[st]));
function triggerDownload (imgURI, fileName)
var evt = new MouseEvent("click",
view: window,
bubbles: false,
cancelable: true
);
var a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("href", imgURI);
a.setAttribute("target", '_blank');
a.dispatchEvent(evt);
function downloadSvg(svg, fileName)
var copy = svg.cloneNode(true);
copyStylesInline(copy, svg);
var canvas = document.createElement("canvas");
var bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, bbox.width, bbox.height);
var data = (new XMLSerializer()).serializeToString(copy);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], type: "image/svg+xml;charset=utf-8");
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function ()
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
var blob = canvas.msToBlob();
navigator.msSaveOrOpenBlob(blob, fileName);
else
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
triggerDownload(imgURI, fileName);
document.removeChild(canvas);
;
img.src = url;
【讨论】:
这在 IE11 中不起作用,因为.msToBlob()
的安全问题
谢谢!!我喜欢它对“本地”SVG HTML 节点和远程 SVG URL 的工作方式。此外,它不需要完整的外部库
你能提供一个使用例子吗?【参考方案4】:
SVG转blob URL和blob URL转png图片的解决方案
const svg=`<svg version="1.1" baseProfile="full"
xmlns="http://www.w3.org/2000/svg">
<rect fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
svgToPng(svg,(imgData)=>
const pngImage = document.createElement('img');
document.body.appendChild(pngImage);
pngImage.src=imgData;
);
function svgToPng(svg, callback)
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) =>
callback(imgData);
URL.revokeObjectURL(url);
);
function getSvgUrl(svg)
return URL.createObjectURL(new Blob([svg], type: 'image/svg+xml' ));
function svgUrlToPng(svgUrl, callback)
const svgImage = document.createElement('img');
// imgPreview.style.position = 'absolute';
// imgPreview.style.top = '-9999px';
document.body.appendChild(svgImage);
svgImage.onload = function ()
const canvas = document.createElement('canvas');
canvas.width = svgImage.clientWidth;
canvas.height = svgImage.clientHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
const imgData = canvas.toDataURL('image/png');
callback(imgData);
// document.body.removeChild(imgPreview);
;
svgImage.src = svgUrl;
【讨论】:
此解决方案不适用于复杂的 SVG(例如带有图像的图案) 这是您的代码,但已优化,但谢谢您...codepen.io/arcaelas-the-looper/pen/wvrqzvm 谢谢@AlejandroReyes 不客气,你可以在github上关注我:arcaelas【参考方案5】:我的用例是从网络加载 svg 数据,这个 ES6 类完成了这项工作。
class SvgToPngConverter
constructor()
this._init = this._init.bind(this);
this._cleanUp = this._cleanUp.bind(this);
this.convertFromInput = this.convertFromInput.bind(this);
_init()
this.canvas = document.createElement("canvas");
this.imgPreview = document.createElement("img");
this.imgPreview.style = "position: absolute; top: -9999px";
document.body.appendChild(this.imgPreview);
this.canvasCtx = this.canvas.getContext("2d");
_cleanUp()
document.body.removeChild(this.imgPreview);
convertFromInput(input, callback)
this._init();
let _this = this;
this.imgPreview.onload = function()
const img = new Image();
_this.canvas.width = _this.imgPreview.clientWidth;
_this.canvas.height = _this.imgPreview.clientHeight;
img.crossOrigin = "anonymous";
img.src = _this.imgPreview.src;
img.onload = function()
_this.canvasCtx.drawImage(img, 0, 0);
let imgData = _this.canvas.toDataURL("image/png");
if(typeof callback == "function")
callback(imgData)
_this._cleanUp();
;
;
this.imgPreview.src = input;
这是你的使用方法
let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData)
// You now have your png data in base64 (imgData).
// Do what ever you wish with it here.
);
如果你想要一个普通的 JavaScript 版本,你可以head over to Babel website 并在那里转译代码。
【讨论】:
【参考方案6】:更改 svg
以匹配您的元素
function svg2img()
var svg = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(svg);
var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
var b64start = 'data:image/svg+xml;base64,';
var image64 = b64start + svg64;
return image64;
;svg2img()
【讨论】:
它对我不起作用,我收到此错误:Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not of type 'Node'.
@Xsmael 尝试切换DOMParser接口developer.mozilla.org/en-US/docs/Web/API/DOMParser
谢谢,这对我有用。无需使用外部库。
@RyanArqueza 怎么样?
@MTZ 您应该考虑将 utf8 部分用于 utf8 字符,例如您的 SVG 包含非 ASCI 字符【参考方案7】:
这是一个无需库即可工作并返回Promise的函数:
/**
* converts a base64 encoded data url SVG image to a PNG image
* @param originalBase64 data url of svg image
* @param width target width in pixel of PNG image
* @return Promise<String> resolves to png data url of the image
*/
function base64SvgToBase64Png (originalBase64, width)
return new Promise(resolve =>
let img = document.createElement('img');
img.onload = function ()
document.body.appendChild(img);
let canvas = document.createElement("canvas");
let ratio = (img.clientWidth / img.clientHeight) || 1;
document.body.removeChild(img);
canvas.width = width;
canvas.height = width / ratio;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
try
let data = canvas.toDataURL('image/png');
resolve(data);
catch (e)
resolve(null);
;
img.src = originalBase64;
);
在 Firefox 上有一个issue for SVGs without set width / height。
请参阅此working example,其中包含 Firefox 问题的修复。
【讨论】:
在 Safari 中对我不起作用,只是挂起,没有错误 感谢测试,我之前没有用 Safari 测试过。所以Javascript控制台也没有报错,会不会很奇怪? 是的,在我的经验中很奇怪,但不幸的是我没有更多的时间可以浪费在它上面 感谢您提供这个绝妙的例子!【参考方案8】:Svg
到png
可以根据条件转换:
-
如果
svg
的格式为SVG (string) paths:
new Path2D()
并将svg
设置为参数
在画布上绘制路径
创建图像并将canvas.toDataURL()
用作src
。
示例:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;
请注意 Path2D
在 ie
中不受支持,而在 edge 中部分支持。 Polyfill 解决了这个问题:
https://github.com/nilzona/path2d-polyfill
-
创建
svg
blob 并使用.drawImage()
在画布上绘制:
很好的描述: https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng
请注意,在 ie 中,canvas.toDataURL(); 的阶段会出现异常;这是因为 IE 的安全限制太高,在画布后将画布视为只读。所有其他浏览器仅在图像是跨源的情况下进行限制。
-
使用
canvg
JavaScript 库。它是独立的库,但具有有用的功能。
喜欢:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
【讨论】:
第三个链接坏了 是的,确实如此。我现在不知道如何到达那里。但是上面的描述对于一些理解来说已经足够了。参考后的未来复制一些上下文的好主意 固定链接旋转。 Wayback 机器是您修复 linkrot 的朋友。【参考方案9】:我最近发现了几个用于 JavaScript 的图像跟踪库,它们确实能够为位图构建一个可接受的近似值,无论是大小还是质量。我正在开发这个 JavaScript 库和 CLI:
https://www.npmjs.com/package/svg-png-converter
它为所有这些提供统一的API,支持浏览器和节点,不依赖于DOM,以及一个命令行工具。
对于转换徽标/卡通/类似图像,它做得很好。对于照片/真实感,需要进行一些调整,因为输出大小会增长很多。
它有一个游乐场,尽管现在我正在开发一个更好、更易于使用的游乐场,因为添加了更多功能:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
【讨论】:
【参考方案10】:有多种方法可以使用 Canvg 库将 SVG 转换为 PNG。
就我而言,我需要从内联 SVG 获取 PNG blob。
库文档提供了example(参见 OffscreenCanvas 示例)。
但是这个方法目前在Firefox中不起作用。是的,您可以在设置中启用 gfx.offscreencanvas.enabled 选项。但是网站上的每个用户都会这样做吗? :)
不过,还有另一种方法也适用于 Firefox。
const el = document.getElementById("some-svg"); //this is our inline SVG
var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;
const svg = new XMLSerializer().serializeToString(el); //convert SVG to string
//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();
let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));
感谢this的最后一行回答
【讨论】:
以上是关于在浏览器中将 SVG 转换为图像(JPEG、PNG 等)的主要内容,如果未能解决你的问题,请参考以下文章