ChatGPT解决这个技术问题 Extra ChatGPT

在浏览器中将 SVG 转换为图像(JPEG、PNG 等)

我想通过 JavaScript 将 SVG 转换为位图图像(如 JPEG、PNG 等)。

你真正想要完成的任务是什么?即使 echo-flows 的回答告诉我们(在某些浏览器中),几乎所有实际情况都可能存在更好、更简单的转换方法。
这是使用 d3 的示例:stackoverflow.com/a/23667012/439699
svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back - 完美运行! [在链接页面上,sourceSVG = $("#your_svg_elem_name").get(0)]

C
Community

以下是通过 JavaScript 执行此操作的方法:

使用 canvg JavaScript 库通过 Canvas 呈现 SVG 图像:https://github.com/gabelerner/canvg 根据以下说明从 Canvas 捕获编码为 JPG(或 PNG)的数据 URI:将 HTML Canvas 捕获为 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)]
P
Pranav 웃

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...
});

canvg 需要第二个参数为 <svg>...</svg 但 jquery html() 函数不添加 svg 标记,因此此代码适用于我,但我需要将 canvg live 编辑为 canvg('canvas', '<svg>'+$("#editor").html()+'</svg>');
@Luckyn 如果您在 svg 元素的父级上调用 $(selector).html(),它将起作用
@Luckyn 和 @jonathanGB,您不必在包装器上使用 html(),或手动构造父 svg 标记——它甚至可能具有您在此 hack 中遗漏的属性。只需使用 $(svg_elem)[0].outerHTML 即可为您提供 svg 及其内容的完整来源。只是说...
w
worstenbrood

这似乎适用于大多数浏览器:

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 HTML 节点和远程 SVG URL 的工作方式。另外它不需要完整的外部库
你能提供一个使用例子吗?
这个 copyStylesInline 帮了我很多!如果不添加,我导出的 png 看起来很奇怪。谢谢!
S
Sundeep Pidugu

SVG转blob URL和blob URL转png图片的解决方案

const svg=` 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(例如带有图像的图案)
这是您的代码,但已优化,但感谢您... codepen.io/arcaelas-the-looper/pen/wvrqzvm
谢谢@AlejandroReyes
不客气,你可以在 github 上关注我:arcaelas
C
Cels

我的用例是从网络加载 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 并在那里转译代码。


M
Mahdi Khalili

更改 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 字符
k
klues

这是一个无需库即可工作并返回 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 问题的修复。


在 Safari 中对我不起作用,只是挂起,没有错误
感谢您的测试,我之前没有使用 Safari 进行测试。所以Javascript控制台也没有错误,这会很奇怪吗?
是的,在我的经验中很奇怪,但不幸的是我没有更多的时间浪费在这上面
感谢您提供这个绝妙的例子!
1)我建议附加到 img.onerror 也不要冒险在这种情况下拖延承诺。 2)您不必将图像附加到document.body。
a
albert

Svgpng 可以根据条件转换:

如果 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();

很好的描述: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 的朋友。
c
cancerbero

我最近发现了几个用于 JavaScript 的图像跟踪库,它们确实能够为位图构建一个可接受的近似值,无论是大小还是质量。我正在开发这个 JavaScript 库和 CLI:

https://www.npmjs.com/package/svg-png-converter

它为所有这些提供了统一的 API,支持浏览器和节点,不依赖于 DOM,以及一个命令行工具。

对于转换徽标/卡通/类似图像,它做得很好。对于照片/真实感,需要进行一些调整,因为输出大小会增长很多。

它有一个游乐场,虽然现在我正在开发一个更好、更易于使用的游乐场,因为添加了更多功能:

https://cancerberosgx.github.io/demos/svg-png-converter/playground/#


f
foske

有几种方法可以使用 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的回答


U
Umair Khan

这是我的 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'));

< button id="map2Png">PNG