【问题标题】:Convert SVG to image (JPEG, PNG, etc.) in the browser在浏览器中将 SVG 转换为图像(JPEG、PNG 等)
【发布时间】:2011-04-27 21:24:13
【问题描述】:

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

【问题讨论】:

标签: javascript svg


【解决方案1】:

以下是通过 JavaScript 实现的方法:

  1. 使用 canvg JavaScript 库通过 Canvas 呈现 SVG 图像:https://github.com/gabelerner/canvg
  2. 根据以下说明从画布中捕获编码为 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" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
   <rect width="100%" height="100%" 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】:

    Svgpng可以根据条件转换:

    1. 如果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;
    

    请注意 Path2Die 中不受支持,而在 edge 中部分支持。 Polyfill 解决了这个问题: https://github.com/nilzona/path2d-polyfill

    1. 创建svg blob 并使用.drawImage() 在画布上绘制:
    • 制作画布元素
    • 从 svg xml 创建一个 svgBlob 对象
    • 从 domUrl.createObjectURL(svgBlob) 创建一个 url 对象;
    • 创建一个 Image 对象并将 url 分配给 image src
    • 将图像绘制到画布中
    • 从画布中获取png数据字符串:canvas.toDataURL();

    很好的描述: https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng

    请注意,在 ie 中,canvas.toDataURL(); 的阶段会出现异常;这是因为 IE 的安全限制太高,在画布后将画布视为只读。所有其他浏览器仅在图像是跨源的情况下进行限制。

    1. 使用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的最后一行回答

      【讨论】:

        猜你喜欢
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        • 2019-12-16
        • 2017-07-02
        • 1970-01-01
        相关资源
        最近更新 更多