我想通过 JavaScript 将 SVG 转换为位图图像(如 JPEG、PNG 等)。
以下是通过 JavaScript 执行此操作的方法:
使用 canvg JavaScript 库通过 Canvas 呈现 SVG 图像:https://github.com/gabelerner/canvg 根据以下说明从 Canvas 捕获编码为 JPG(或 PNG)的数据 URI:将 HTML Canvas 捕获为 gif /jpg/png/pdf?
jbeard4 解决方案效果很好。
我正在使用 Raphael SketchPad 创建 SVG。链接到步骤 1 中的文件。
对于保存按钮(svg 的 id 是“editor”,画布的 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...
});
<svg>...</svg
但 jquery html() 函数不添加 svg 标记,因此此代码适用于我,但我需要将 canvg live 编辑为 canvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
$(selector).html()
,它将起作用
html()
,或手动构造父 svg
标记——它甚至可能具有您在此 hack 中遗漏的属性。只需使用 $(svg_elem)[0].outerHTML
即可为您提供 svg 及其内容的完整来源。只是说...
这似乎适用于大多数浏览器:
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;
}
.msToBlob()
的安全问题,这在 IE11 中不起作用
SVG转blob URL和blob URL转png图片的解决方案
const svg=`` svgToPng(svg,(imgData)=>{ const pngImage = document.createElement('img '); document.body.appendChild(pngImage); pngImage.src=imgData; });函数 svgToPng(svg, 回调) { 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;常量 canvasCtx = canvas.getContext('2d'); canvasCtx.drawImage(svgImage, 0, 0); const imgData = canvas.toDataURL('image/png');回调(imgData); // document.body.removeChild(imgPreview); }; svgImage.src = svgUrl; }
我的用例是从网络加载 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 并在那里转译代码。
更改 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'.
这是一个无需库即可工作并返回 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.onerror = function() {
resolve(null);
};
img.src = originalBase64;
});
}
在 Firefox 上有一个 issue for SVGs without set width / height。
请参阅此working example,其中包括 Firefox 问题的修复。
Svg
到 png
可以根据条件转换:
如果 svg 格式为 SVG(字符串)路径:
创建画布
创建新的 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;
请注意,ie
不支持 Path2D
,edge 部分支持。 Polyfill 解决了这个问题:https://github.com/nilzona/path2d-polyfill
使用 .drawImage() 创建 svg blob 并在画布上绘制:
制作画布元素
从 svg xml 创建一个 svgBlob 对象
从 domUrl.createObjectURL(svgBlob) 创建一个 url 对象;
创建一个 Image 对象并将 url 分配给图像 src
将图像绘制到画布中
从画布中获取 png 数据字符串:canvas.toDataURL();
请注意,在 ie 中,您将在 canvas.toDataURL(); 的阶段获得异常;这是因为 IE 的安全限制太高,在画布后将画布视为只读。所有其他浏览器仅在图像是跨源时才进行限制。
使用 canvg JavaScript 库。它是独立的库,但具有有用的功能。
喜欢:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
我最近发现了几个用于 JavaScript 的图像跟踪库,它们确实能够为位图构建一个可接受的近似值,无论是大小还是质量。我正在开发这个 JavaScript 库和 CLI:
https://www.npmjs.com/package/svg-png-converter
它为所有这些提供了统一的 API,支持浏览器和节点,不依赖于 DOM,以及一个命令行工具。
对于转换徽标/卡通/类似图像,它做得很好。对于照片/真实感,需要进行一些调整,因为输出大小会增长很多。
它有一个游乐场,虽然现在我正在开发一个更好、更易于使用的游乐场,因为添加了更多功能:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
有几种方法可以使用 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的回答
这是我的 2 美分。不知何故,Download
锚标记在代码段中未按预期工作,但在 Chrome
中工作正常。
这是工作jsFiddle
const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve); const svgToImgDownload = ext => { if (!['png', 'jpg', 'webp'].includes(ext)) return; const _svg = document.querySelector("#svg_container").querySelector('svg'); const xmlSerializer = new XMLSerializer();让 _svgStr = xmlSerializer.serializeToString(_svg); const img = document.createElement('img'); img.src = 'data:image/svg+xml;base64,' + window.btoa(_svgStr); waitForImage(img) .then(_ => { const canvas = document.createElement('canvas'); canvas.width = _svg.clientWidth; canvas.height = _svg.clientHeight; canvas.getContext('2d').drawImage( img, 0, 0, _svg.clientWidth, _svg.clientHeight); return canvas.toDataURL('image/' + (ext == 'jpg' ? 'jpeg' : ext), 1.0); }) .then(dataURL = > { console.log(dataURL); document.querySelector("#img_download_btn").innerHTML = `下载`; }) .catch(console.error); }; document.querySelector('#map2Png').addEventListener('click', _ => svgToImgDownload('png')); document.querySelector('#map2Jpg').addEventListener('click', _ => svgToImgDownload('jpg')); document.querySelector('#map2Webp').addEventListener('click', _ => svgToImgDownload('webp'));
toImage()
和download()
。