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

Zai*_*ain 280 javascript svg

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

jbe*_*rd4 237

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

  1. 使用canvg JavaScript库使用Canvas渲染SVG图像:https://github.com/gabelerner/canvg
  2. 根据以下说明捕获从Canvas编码为JPG(或PNG)的数据URI:将HTML Canvas捕获为gif/jpg/png/pdf?

  • 谢谢你不支持IE8.人们应该明白是时候继续前进了. (106认同)
  • 这不是严格的Javascript,而是HTML5.这不适用于IE8或任何其他不支持HTML5 Canvas的浏览器. (28认同)
  • 如果浏览器支持SVG和canvas,那么将一种更简单的方法将SVG加载到内存中,然后将其绘制到画布中,而不需要Canvg,这是一个非常大的库,因为它处理所有的SVG解析支持SVG的浏览器已经免费提供.我不确定这是否满足原始用例,但如果是,那么[请参阅此资源了解详细信息](https://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/index.html). (14认同)
  • 您现在可以使用JavaScript SVG库[Pablo](http://pablojs.com)来实现这一点(我做到了).请参阅[`toImage()`](http://pablojs.com/api/toImage/)以及[`download()`](http://pablojs.com/api/download/)以获取自动下载的图片. (6认同)
  • http://svgopen.org/2010/papers/62-From_SVG_to_Canvas_and_Back/-完美运行![在链接页面上,sourceSVG = $(“#your_svg_elem_name”)。get(0)] (2认同)
  • canvg 不完整。例如它不支持面具。 (2认同)

coo*_*oop 43

jbeard4解决方案工作得很漂亮.

我正在使用Raphael SketchPad创建一个SVG.链接到步骤1中的文件.

对于Save按钮(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...
});
Run Code Online (Sandbox Code Playgroud)

  • canvg 需要第二个参数为 `<svg>...</svg` 但 jquery html() 函数不添加 svg 标签,所以这段代码对我有用,但我需要将 canvg live 编辑为 `canvg('画布', '<svg>'+$("#editor").html()+'</svg>');` (2认同)
  • @Lucyn如果你在 *svg* 元素的父元素上调用 `$(selector).html()` ,它将起作用 (2认同)
  • @Luckyn 和 @jonathanGB,您不必在包装器上使用 `html()`,或者手动构建父 `svg` 标记 - 它甚至可能具有您在本次黑客攻击中遗漏的属性。只需使用 `$(svg_elem)[0].outerHTML` 即可获得 svg 及其内容的完整源代码。只是说... (2认同)

Teo*_*cci 33

这是一个老问题了,2022 年我们有了 ES6,我们不需要 3rd 方库。

svg这是将图像转换为其他格式的非常基本的方法。

技巧是将svg元素作为img元素加载,然后使用画布元素将图像转换为所需的格式。因此,需要四个步骤:

  1. 提取svgxml数据字符串。
  2. 将数据字符串加载到元素xmlimg
  3. 使用 a元素将元素转换img为 adataURLcanvas
  4. 将转换后的元素加载dataURL到新img元素中

步骤1

提取svg数据xml字符串很简单,我们不需要将其转换为 Base64 字符串。我们只需将其序列化为 XML,然后将字符串编码为 URI:

// Data header for a svg image: 
const dataHeader = 'data:image/svg+xml;charset=utf-8'

// Serialize it as xml string:
const serializeAsXML = $e => (new XMLSerializer()).serializeToString($e)

// Encode URI data as UTF8 data:
const encodeAsUTF8 = s => `${dataHeader},${encodeURIComponent(s)}`

// Select the element:
const $svg = document.getElementById('svg-container').querySelector('svg')

// Encode it as a data string:
const svgData = encodeAsUTF8(serializeAsXML($svg))
Run Code Online (Sandbox Code Playgroud)

笔记:

如果您需要 base64 数据,可以使用此选项:

...

// Encode URI data as base64 data:
const encodeAsB64 = s => `${dataHeader};base64,${btoa(s)}`
...

// Encode it as a data string:
const svgData = encodeAsB64(serializeAsXML($svg))
Run Code Online (Sandbox Code Playgroud)

第2步

xml数据字符串加载到元素中img

// This function returns a Promise whenever the $img is loaded
const loadImage = async url => {
    const $img = document.createElement('img')
    $img.src = url
    return new Promise((resolve, reject) => {
        $img.onload = () => resolve($img)
        $img.onerror = reject
        $img.src = url
    })
}
Run Code Online (Sandbox Code Playgroud)

步骤3

使用 a元素将元素转换img为 a :dataURLcanvas

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/${format}`, 1.0)
Run Code Online (Sandbox Code Playgroud)

步骤4

将转换后的元素加载dataURL到新img元素中:

const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
Run Code Online (Sandbox Code Playgroud)

这里有一个工作片段:

const dataHeader = 'data:image/svg+xml;charset=utf-8'
const $svg = document.getElementById('svg-container').querySelector('svg')
const $holder = document.getElementById('img-container')
const $label = document.getElementById('img-format')

const destroyChildren = $element => {
  while ($element.firstChild) {
    const $lastChild = $element.lastChild ?? false
    if ($lastChild) $element.removeChild($lastChild)
  }
}

const loadImage = async url => {
  const $img = document.createElement('img')
  $img.src = url
  return new Promise((resolve, reject) => {
    $img.onload = () => resolve($img)
    $img.onerror = reject
  })
}

const serializeAsXML = $e => (new XMLSerializer()).serializeToString($e)

const encodeAsUTF8 = s => `${dataHeader},${encodeURIComponent(s)}`
const encodeAsB64 = s => `${dataHeader};base64,${btoa(s)}`

const convertSVGtoImg = async e => {
  const $btn = e.target
  const format = $btn.dataset.format ?? 'png'
  $label.textContent = format

  destroyChildren($holder)

  const svgData = encodeAsUTF8(serializeAsXML($svg))

  const img = await loadImage(svgData)
  
  const $canvas = document.createElement('canvas')
  $canvas.width = $svg.clientWidth
  $canvas.height = $svg.clientHeight
  $canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
  
  const dataURL = await $canvas.toDataURL(`image/${format}`, 1.0)
  console.log(dataURL)
  
  const $img = document.createElement('img')
  $img.src = dataURL
  $holder.appendChild($img)
}

const buttons = [...document.querySelectorAll('[data-format]')]
for (const $btn of buttons) {
  $btn.onclick = convertSVGtoImg
}
Run Code Online (Sandbox Code Playgroud)
.wrapper {
  display: flex;
  flex-flow: row nowrap;
  width: 100vw;
}

.images {
  display: flex;
  flex-flow: row nowrap;
  width: 70%;
}

.image {
  width: 50%;
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
}

.label {
  width: 100%;
  text-align: center;
}
Run Code Online (Sandbox Code Playgroud)
<div class="wrapper">
  <div class="item images">
    <div class="image left">
      <div class="label">svg</div>
      <div id="svg-container">
        <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200" height="200" viewBox="0 0 248 204">
  <path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/>
</svg>
      </div>
    </div>
    <div class="image right">
      <div id="img-format" class="label"></div>
      <div id="img-container"></div>
    </div>
  </div>
  <div class="item buttons">
    <button id="btn-png" data-format="png">PNG</button>
    <button id="btn-jpg" data-format="jpeg">JPG</button>
    <button id="btn-webp" data-format="webp">WEBP</button>
  </div>
</div>
Run Code Online (Sandbox Code Playgroud)


小智 15

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

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 您能提供一个使用示例吗? (6认同)
  • 由于.msToBlob()的安全性问题,这在IE11中不起作用。 (3认同)
  • 这个 copyStylesInline 对我帮助很大!如果不添加它,我导出的 png 看起来很奇怪。谢谢你! (3认同)
  • 谢谢!!我喜欢这对于“本地”SVG HTML 节点和远程 SVG URL 的工作方式。另外它不需要完整的外部库 (2认同)

Tho*_*ter 15

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;
 }
Run Code Online (Sandbox Code Playgroud)


Cel*_*els 9

我的用例是从网络加载 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是你如何使用它

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.
});
Run Code Online (Sandbox Code Playgroud)

如果你想要一个普通的 JavaScript 版本,你可以前往 Babel 网站并在那里转译代码。


Mah*_*ili 5

更改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()
Run Code Online (Sandbox Code Playgroud)

  • 它对我不起作用,我收到此错误:`Uncaught TypeError: Failed to execute 'serializeToString' on 'XMLSerializer': parameter 1 is not type 'Node'.` (2认同)

klu*_*ues 5

这是一个无需库即可工作并返回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;
    });
}
Run Code Online (Sandbox Code Playgroud)

在 Firefox上,没有设置 width / height 的 SVG存在问题

请参阅此工作示例,其中包括对 Firefox 问题的修复。