将 SVG 转换为 PNG,并将应用的图像作为 svg 元素的背景
Posted
技术标签:
【中文标题】将 SVG 转换为 PNG,并将应用的图像作为 svg 元素的背景【英文标题】:Convert SVG to PNG with applied images as background to svg elements 【发布时间】:2016-03-06 16:54:09 【问题描述】:我有一个外部 SVG 文件,其中包含一些嵌入的图像标签。每当我使用 toDataURL()
将此 SVG 转换为 PNG 时,生成的 PNG 图像不包含我作为模式应用于某些 SVG 路径的图像。有什么办法可以解决这个问题吗?
【问题讨论】:
我假设您正在使用这个htmlCanvasElement.toDataURL()
,但为了帮助您调试问题,您应该粘贴用于画布和转换的代码。否则很难为您提供帮助...
【参考方案1】:
是的,有:将 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 xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="Pattern" x="0" y="0" >
<image xlink:href="https://dl.dropboxusercontent.com/s/1alt1303g9zpemd/UFBxY.png" />
</pattern>
</defs>
<rect fill="url(#Pattern)" x="0" y="0" />
</svg>
【讨论】:
优秀的解决方案。 +1 虽然我会说最好在if
、else
等之后的单个语句中包含 ,因为这是新手最容易被忽视的错误来源之一。
@Blindman67 你是对的,我的坏习惯,快速从整个脚本中复制粘贴,仍然需要粗略的清理......
@Kaiido 我遇到了类似的问题link 通过不同的类更改我的内容的背景图像后,它不显示图像,只是一个空格:)
@Kaiido,你的代码很棒,但我认为它会受益于一些异步方法,对吧?以上是关于将 SVG 转换为 PNG,并将应用的图像作为 svg 元素的背景的主要内容,如果未能解决你的问题,请参考以下文章