在将SVG转换为PNG时裁剪图像

时间:2016-06-14 19:21:52

标签: javascript canvas svg

将SVG保存到PNG时,保存的图像仅包含在视框/窗口中呈现的SVG。如何保存包含整个SVG的大型PNG?

// SVG element and XML string.
var svg = document.querySelector('svg');
var svgData = new XMLSerializer().serializeToString(svg);

// Canvas to hold the image.
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

// Canvas size = SVG size.
var svgSize = svg.getBoundingClientRect();
canvas.width = svgSize.width;
canvas.height = svgSize.height;

// Image element appended with data.
var img = document.createElement('img');
img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(svgData));

img.onload = function() {
    // Draw image on canvas and convert to URL.
    context.drawImage(img,0,0);
    console.log(canvas.toDataURL('image/png'));
};

2 个答案:

答案 0 :(得分:1)

而不是:

var svgSize = svg.getBoundingClientRect();

使用:

var svgSize = svg.viewBox.baseVal;

这将为您提供viewBox的真实尺寸。

参考

https://stackoverflow.com/a/7682976/2813224



// SVG element and XML string.
var svg = document.querySelector('svg');
var svgData = new XMLSerializer().serializeToString(svg);

// Canvas to hold the image.
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

// Canvas size = SVG size.
var svgSize = svg.viewBox.baseVal;
canvas.width = svgSize.width;
canvas.height = svgSize.height;

// Image element appended with data.
var img = document.createElement('img');
img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(svgData));

img.onload = function() {
    // Draw image on canvas and convert to URL.
    context.drawImage(img,0,0);
    console.log(canvas.toDataURL('image/png'));
};

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="64px" height="64px" viewBox="-0.5 0.5 64 64" enable-background="new -0.5 0.5 64 64" xml:space="preserve">
<g>
	<circle fill="#FFFFFF" cx="31.325" cy="32.873" r="30.096"/>
	<path id="text2809_1_" d="M31.5,14.08c-10.565,0-13.222,9.969-13.222,18.42c0,8.452,2.656,18.42,13.222,18.42   c10.564,0,13.221-9.968,13.221-18.42C44.721,24.049,42.064,14.08,31.5,14.08z M31.5,21.026c0.429,0,0.82,0.066,1.188,0.157   c0.761,0.656,1.133,1.561,0.403,2.823l-7.036,12.93c-0.216-1.636-0.247-3.24-0.247-4.437C25.808,28.777,26.066,21.026,31.5,21.026z    M36.766,26.987c0.373,1.984,0.426,4.056,0.426,5.513c0,3.723-0.258,11.475-5.69,11.475c-0.428,0-0.822-0.045-1.188-0.136   c-0.07-0.021-0.134-0.043-0.202-0.067c-0.112-0.032-0.23-0.068-0.336-0.11c-1.21-0.515-1.972-1.446-0.874-3.093L36.766,26.987z"/>
	<path id="path2815_1_" d="M31.433,0.5c-8.877,0-16.359,3.09-22.454,9.3c-3.087,3.087-5.443,6.607-7.082,10.532   C0.297,24.219-0.5,28.271-0.5,32.5c0,4.268,0.797,8.32,2.397,12.168c1.6,3.85,3.921,7.312,6.969,10.396   c3.085,3.049,6.549,5.399,10.398,7.037c3.886,1.602,7.939,2.398,12.169,2.398c4.229,0,8.34-0.826,12.303-2.465   c3.962-1.639,7.496-3.994,10.621-7.081c3.011-2.933,5.289-6.297,6.812-10.106C62.73,41,63.5,36.883,63.5,32.5   c0-4.343-0.77-8.454-2.33-12.303c-1.562-3.885-3.848-7.32-6.857-10.33C48.025,3.619,40.385,0.5,31.433,0.5z M31.567,6.259   c7.238,0,13.412,2.566,18.554,7.709c2.477,2.477,4.375,5.31,5.67,8.471c1.296,3.162,1.949,6.518,1.949,10.061   c0,7.354-2.516,13.454-7.506,18.33c-2.592,2.516-5.502,4.447-8.74,5.781c-3.2,1.334-6.498,1.994-9.927,1.994   c-3.468,0-6.788-0.653-9.949-1.948c-3.163-1.334-6.001-3.238-8.516-5.716c-2.515-2.514-4.455-5.353-5.826-8.516   c-1.333-3.199-2.017-6.498-2.017-9.927c0-3.467,0.684-6.787,2.017-9.949c1.371-3.2,3.312-6.074,5.826-8.628   C18.092,8.818,24.252,6.259,31.567,6.259z"/>
</g>
</svg>
&#13;
&#13;
&#13;

答案 1 :(得分:1)

这是因为您将画布大小设置为渲染的svg。 在你的CSS中,你可能会调整你的svg的大小,这会导致它的计算大小与它的自然大小之间存在差异。
默认情况下,如果未传递这些参数,drawImage(img, dx, dy, dWidth, dHeight)将使用源的宽度和高度作为destinationWidth和destinationHeight。

您可以查看此示例,其中显示与光栅图像相同的行为:

window.onload = function(){

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

var svgSize = inDoc.getBoundingClientRect();
canvas.width = svgSize.width;
canvas.height = svgSize.height;

var img = document.createElement('img');
img.setAttribute('src',  inDoc.src);

img.onload = function() {
  context.drawImage(img,0,0);
  document.body.appendChild(canvas);
};
}
img{ width: 64px; height: 64px}
<img id="inDoc" src="http://lorempixel.com/128/128"/>

这里有一个小图,显示正在发生的事情。

explanation of the SVGElement size vs img.svg size

因此解决方案只是将画布“widthheight属性设置为img的属性,就像使用其他任何来源一样:

img.onload = function(){
    canvas.width = this.width;
    canvas.height = this.height;
    ctx.drawImage(this, 0,0);
    }

如果你想要包含一些缩放因子:

img.onload = function(){
    canvas.width = this.width * scale;
    canvas.height = this.height * scale;
    ctx.drawImage(this, 0,0, canvas.width, canvas.height);
    }

现在,一个与您的实际代码无关,但仍然光栅图像和svg图像之间的巨大差异是svg widthheight可以设置为相对单位(如%) 浏览器没有直接关于它相对于什么的线索。 ( Chrome会猜测,我不知道如何,其他人不会渲染您的图片)。

所以你需要在导出到dataURI之前检查一下:

var absoluteUnits = [1,5,6,7,8,9,10];
if (absoluteUnits.indexOf(svg.width.baseVal.unitType)<0) {
    svg.setAttribute('width', aboluteWidth);
    }
if (absoluteUnits.indexOf(svg.height.baseVal.unitType)<0) {
    svg.setAttribute('height', aboluteHeight);
    }

此处absoluteWidthabsoluteHeight可以是svg.getBoundingClientRect()的结果。

另请注意,IE9无法显示img.widthimg.height值,因此您必须为其制作一个特殊情况...... ...但是既然你应该已经检查过absoluteSize,这应该不是问题:

var svg = document.querySelector('svg');
// we'll use a copy to not modify the svg in the document
var copy = svg.cloneNode(true);
var absoluteWidth, absoluteHeight, BBox;
var absoluteUnits = [1,5,6,7,8,9,10];

if (absoluteUnits.indexOf(svg.width.baseVal.unitType)<0) {
  BBox = svg.getBoundingClientRect();
  absoluteWidth = BBox.width
  copy.setAttribute('width', absoluteWidth);
  }
else{
  absoluteWidth = svg.getAttribute('width');
  }
if (absoluteUnits.indexOf(svg.height.baseVal.unitType)<0) {
  if(!BBox){
     BBox = svg.getBoundingClientRect();
    }
  absoluteHeight = BBox.height;
  copy.setAttribute('height', absoluteHeight)
  }
else{
  absoluteHeight = svg.getAttribute('height');
  }

var svgData = new XMLSerializer().serializeToString(copy);

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');

var img = document.createElement('img');
img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(svgData));

img.onload = function() {
    // here you set your canvas width and height
    canvas.width = this.width || absoluteWidth;
    canvas.height = this.height || absoluteHeight;

    context.drawImage(img,0,0);
    document.body.appendChild(canvas);
};
svg{width: 64px; height:64px; border: 1px solid green;}
canvas{border: 1px solid blue;}
<svg width="128px" height="128px" viewBox="0 0 128 128">
  <rect x="20" y="20" width="84" height="84"/>
</svg>