javascript - 将 SVG 转换为 PNG,并将应用图像作为 svg 元素的背景

标签 javascript html canvas svg snap.svg

我有一个外部 SVG 文件,其中包含一些以图案形式嵌入的图像标签。每当我使用 toDataURL() 将此 SVG 转换为 PNG 时,生成的 PNG 图像不包含我已作为模式应用到某些 SVG 路径的图像。有什么办法可以解决这个问题吗?

最佳答案

是的,有:将 svg 附加到您的文档并将所有包含的图像编码为 dataURI。

我正在写 a script这样做还有一些其他的东西,比如包括外部样式表和 toDataURL 失败的其他一些修复(例如通过 xlink:href 属性或 <funciri> 引用的外部元素)。

这是我为解析图像内容而编写的函数:

function parseImages(){
    var xlinkNS = "http://www.w3.org/1999/xlink";
    var total, encoded;
    // convert an external bitmap image to a dataURL
    var toDataURL = function (image) {

        var img = new Image();
        // CORS workaround, this won't work in IE<11
        // If you are sure you don't need it, remove the next line and the double onerror handler
        // First try with crossorigin set, it should fire an error if not needed
        img.crossOrigin = 'Anonymous';

        img.onload = function () {
            // we should now be able to draw it without tainting the canvas
            var canvas = document.createElement('canvas');
            canvas.width = this.width;
            canvas.height = this.height;
            // draw the loaded image
            canvas.getContext('2d').drawImage(this, 0, 0);
            // set our <image>'s href attribute to the dataURL of our canvas
            image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
            // that was the last one
            if (++encoded === total) exportDoc();
        };

        // No CORS set in the response      
        img.onerror = function () {
            // save the src
            var oldSrc = this.src;
            // there is an other problem
            this.onerror = function () {
                console.warn('failed to load an image at : ', this.src);
                if (--total === encoded && encoded > 0) exportDoc();
            };
            // remove the crossorigin attribute
            this.removeAttribute('crossorigin');
            // retry
            this.src = '';
            this.src = oldSrc;
        };
        // load our external image into our img
        img.src = image.getAttributeNS(xlinkNS, 'href');
    };

    // get an external svg doc to data String
    var parseFromUrl = function(url, element){
        var xhr = new XMLHttpRequest();
        xhr.onload = function(){
            if(this.status === 200){
                var response = this.responseText || this.response;
                var dataUrl = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(response);
                element.setAttributeNS(xlinkNS, 'href', dataUrl);
                if(++encoded === total) exportDoc();
                }
            // request failed with xhr, try as an <img>
            else{
                toDataURL(element);
                }
            };
        xhr.onerror = function(){toDataURL(element);};
        xhr.open('GET', url);
        xhr.send();
        };

    var images = svg.querySelectorAll('image');
    total = images.length;
    encoded = 0;

    // loop through all our <images> elements
    for (var i = 0; i < images.length; i++) {
        var href = images[i].getAttributeNS(xlinkNS, 'href');
        // check if the image is external
        if (href.indexOf('data:image') < 0){
            // if it points to another svg element
            if(href.indexOf('.svg') > 0){
                parseFromUrl(href, images[i]);
                }
            else // a pixel image
                toDataURL(images[i]);
            }
        // else increment our counter
        else if (++encoded === total) exportDoc();
    }
    // if there were no <image> element
    if (total === 0) exportDoc();
}

这里的 svgDoc 叫做 svg ,
exportDoc()函数可以写成:

var exportDoc = function() {
    // check if our svgNode has width and height properties set to absolute values
    // otherwise, canvas won't be able to draw it
    var bbox = svg.getBoundingClientRect();

    if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width);
    if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height);

    // serialize our node
    var svgData = (new XMLSerializer()).serializeToString(svg);
    // remember to encode special chars
    var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);

    var svgImg = new Image();

    svgImg.onload = function () {
        var canvas =  document.createElement('canvas');
        // IE11 doesn't set a width on svg images...
        canvas.width = this.width || bbox.width;
        canvas.height = this.height || bbox.height;

        canvas.getContext('2d').drawImage(svgImg, 0, 0, canvas.width, canvas.height);
        doSomethingWith(canvas)
    };

    svgImg.src = svgURL;
};

但同样,您必须首先将您的 svg 附加到文档中(通过 xhr 或附加到 <iframe><object> 元素中,并且您必须确保所有资源都是 CORS兼容(或来自同一域)以便呈现这些内容。

var svg = document.querySelector('svg');
var doSomethingWith = function(canvas) {
  document.body.appendChild(canvas)
};

function parseImages() {
  var xlinkNS = "http://www.w3.org/1999/xlink";
  var total, encoded;
  // convert an external bitmap image to a dataURL
  var toDataURL = function(image) {

    var img = new Image();
    // CORS workaround, this won't work in IE<11
    // If you are sure you don't need it, remove the next line and the double onerror handler
    // First try with crossorigin set, it should fire an error if not needed
    img.crossOrigin = 'anonymous';

    img.onload = function() {
      // we should now be able to draw it without tainting the canvas
      var canvas = document.createElement('canvas');
      canvas.width = this.width;
      canvas.height = this.height;
      // draw the loaded image
      canvas.getContext('2d').drawImage(this, 0, 0);
      // set our <image>'s href attribute to the dataURL of our canvas
      image.setAttributeNS(xlinkNS, 'href', canvas.toDataURL());
      // that was the last one
      if (++encoded === total) exportDoc();
    };

    // No CORS set in the response		
    img.onerror = function() {
      // save the src
      var oldSrc = this.src;
      // there is an other problem
      this.onerror = function() {
        console.warn('failed to load an image at : ', this.src);
        if (--total === encoded && encoded > 0) exportDoc();
      };
      // remove the crossorigin attribute
      this.removeAttribute('crossorigin');
      // retry
      this.src = '';
      this.src = oldSrc;
    };
    // load our external image into our img
    var href = image.getAttributeNS(xlinkNS, 'href');
    // really weird bug that appeared since this answer was first posted
    // we need to force a no-cached request for the crossOrigin be applied
    img.src = href + (href.indexOf('?') > -1 ? + '&1': '?1');
  };

  // get an external svg doc to data String
  var parseFromUrl = function(url, element) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      if (this.status === 200) {
        var response = this.responseText || this.response;
        var dataUrl = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(response);
        element.setAttributeNS(xlinkNS, 'href', dataUrl);
        if (++encoded === total) exportDoc();
      }
      // request failed with xhr, try as an <img>
      else {
        toDataURL(element);
      }
    };
    xhr.onerror = function() {
      toDataURL(element);
    };
    xhr.open('GET', url);
    xhr.send();
  };

  var images = svg.querySelectorAll('image');
  total = images.length;
  encoded = 0;

  // loop through all our <images> elements
  for (var i = 0; i < images.length; i++) {
    var href = images[i].getAttributeNS(xlinkNS, 'href');
    // check if the image is external
    if (href.indexOf('data:image') < 0) {
      // if it points to another svg element
      if (href.indexOf('.svg') > 0) {
        parseFromUrl(href, images[i]);
      } else // a pixel image
        toDataURL(images[i]);
    }
    // else increment our counter
    else if (++encoded === total) exportDoc();
  }
  // if there were no <image> element
  if (total === 0) exportDoc();
}

var exportDoc = function() {
  // check if our svgNode has width and height properties set to absolute values
  // otherwise, canvas won't be able to draw it
  var bbox = svg.getBoundingClientRect();

  if (svg.width.baseVal.unitType !== 1) svg.setAttribute('width', bbox.width);
  if (svg.height.baseVal.unitType !== 1) svg.setAttribute('height', bbox.height);

  // serialize our node
  var svgData = (new XMLSerializer()).serializeToString(svg);
  // remember to encode special chars
  var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);

  var svgImg = new Image();

  svgImg.onload = function() {
    var canvas = document.createElement('canvas');
    // IE11 doesn't set a width on svg images...
    canvas.width = this.width || bbox.width;
    canvas.height = this.height || bbox.height;

    canvas.getContext('2d').drawImage(svgImg, 0, 0, canvas.width, canvas.height);
    doSomethingWith(canvas)
  };

  svgImg.src = svgURL;
};
window.onload = parseImages;
canvas {
  border: 1px solid green !important;
}
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>

    <pattern id="Pattern" x="0" y="0" width=".25" height=".25">
      <image xlink:href="https://dl.dropboxusercontent.com/s/1alt1303g9zpemd/UFBxY.png" width="100" height="100"/>
    </pattern>

  </defs>

  <rect fill="url(#Pattern)" x="0" y="0" width="200" height="200"/>
</svg>

关于javascript - 将 SVG 转换为 PNG,并将应用图像作为 svg 元素的背景,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34042910/

相关文章:

javascript - jQuery,如何设置多个输入需要通知输入

html - Windows 中的 Chrome 63 忽略了自动完成 ="new-password"

javascript - 在 Canvas html/css 旁边放置按钮

javascript - 如何将 Canvas 恢复到另一个函数中保存的状态?

javascript - 在输入中传递 HTML DOM 属性

javascript - 目标 <td> 位于同一 <tr> 中

javascript - Angularjs 1.X 相当于 Angular 2 HostBinding 和 HostListener

javascript - 为什么这个 jQuery 代码片段在 IE8 中不能像在 Firefox 或 Chrome 中那样工作(包括现场演示)?

html - 旋转 div 在 IE 上产生黑色背景,我该如何删除它?

javascript - 如何清除 Canvas ?