我正在尝试使用画布将外部 svg 图标转换为 base64 png。它适用于除 Firefox 之外的所有浏览器,它会抛出错误“NS_ERROR_NOT_AVAILABLE”。
var img = new Image();
img.src = "icon.svg";
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL;
};
有人可以帮我解决这个问题吗?预先感谢。
Firefox 现在应该可以毫无问题地将这个 SVG 绘制到画布上。
Firefox 过去仅支持在 svg 文件的根
<svg>
元素上具有宽度/高度属性并且这些宽度/高度属性不是百分比时将 SVG 图像绘制到画布上。不过这个错误不久前就被修复了。
如前所述,这是一个未解决的错误,是由于 Firefox 在绘制到画布时接受的 SVG 尺寸规范受到限制而导致的。有一个解决方法。
Firefox 需要 SVG 本身具有明确的宽度和高度属性。我们可以通过获取 XML 形式的 SVG 并对其进行修改来添加这些内容。
var img = new Image();
var src = "icon.svg";
// request the XML of your svg file
var request = new XMLHttpRequest();
request.open('GET', src, true)
request.onload = function() {
// once the request returns, parse the response and get the SVG
var parser = new DOMParser();
var result = parser.parseFromString(request.responseText, 'text/xml');
var inlineSVG = result.getElementsByTagName("svg")[0];
// add the attributes Firefox needs. These should be absolute values, not relative
inlineSVG.setAttribute('width', '48px');
inlineSVG.setAttribute('height', '48px');
// convert the SVG to a data uri
var svg64 = btoa(new XMLSerializer().serializeToString(inlineSVG));
var image64 = 'data:image/svg+xml;base64,' + svg64;
// set that as your image source
img.src = img64;
// do your canvas work
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL;
};
}
// send the request
request.send();
这是该解决方案的最基本版本,不包括检索 XML 时的错误处理。 这个 inline-svg 处理程序(大约第 110 行)演示了更好的错误处理,我从中导出了该方法的一部分。
这不是最强大的解决方案,但这个 hack 达到了我们的目的。提取
viewBox
数据并将这些尺寸用于宽度/高度属性。
仅当遇到的第一个
viewBox
的大小能够准确表示 SVG 文档的大小时,此方法才有效,但并非所有情况都如此。
// @svgDoc is some SVG document.
let svgSize = getSvgViewBox(svgDoc);
// No SVG size?
if (!svgSize.width || !svgSize.height) {
console.log('Image is missing width or height');
// Have size, resolve with new SVG image data.
} else {
// Rewrite SVG doc
let unit = 'px';
$('svg', svgDoc).attr('width', svgSize.width + unit);
$('svg', svgDoc).attr('height', svgSize.height + unit);
// Get data URL for new SVG.
let svgDataUrl = svgDocToDataURL(svgDoc);
}
function getSvgViewBox(svgDoc) {
if (svgDoc) {
// Get viewBox from SVG doc.
let viewBox = $(svgDoc).find('svg').prop('viewBox').baseVal;
// Have viewBox?
if (viewBox) {
return {
width: viewBox.width,
height: viewBox.height
}
}
}
// If here, no viewBox found so return null case.
return {
width: null,
height: null
}
}
function svgDocToDataURL(svgDoc, base64) {
// Set SVG prefix.
const svgPrefix = "data:image/svg+xml;";
// Serialize SVG doc.
var svgData = new XMLSerializer().serializeToString(svgDoc);
// Base64? Return Base64-encoding for data URL.
if (base64) {
var base64Data = btoa(svgData);
return svgPrefix + "base64," + base64Data;
// Nope, not Base64. Return URL-encoding for data URL.
} else {
var urlData = encodeURIComponent(svgData);
return svgPrefix + "charset=utf8," + urlData;
}
}