Firefox 使用 drawImage 将 SVG 图像渲染到 HTML5 canvas 时出错
Firefox error rendering an SVG image to HTML5 canvas with drawImage
我正在尝试使用 canvas 将外部 svg 图标转换为 base64 png。它适用于除 Firefox 之外的所有浏览器,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 图像绘制到 canvas,除非 svg 文件在根 <svg>
元素上具有 width/height 属性,并且这些 width/height 属性不是百分比。这是一个longstanding bug.
您需要编辑 icon.svg 文件,使其符合上述条件。
如前所述,这是一个开放性错误,由 Firefox 在绘制 canvas 时接受的 SVG 尺寸规范的限制引起。有一个解决方法。
Firefox 要求 SVG 本身具有明确的宽度和高度属性。我们可以通过将 SVG 获取为 XML 并修改它来添加这些。
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 时的错误处理。 this inline-svg handler(大约第 110 行)中演示了更好的错误处理,我从中派生了此方法的一部分。
这不是最可靠的解决方案,但这个 hack 对我们的目的有用。提取 viewBox
数据并将这些维度用于 width/height 属性。
这仅在第一个 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;
}
}
我正在尝试使用 canvas 将外部 svg 图标转换为 base64 png。它适用于除 Firefox 之外的所有浏览器,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 图像绘制到 canvas,除非 svg 文件在根 <svg>
元素上具有 width/height 属性,并且这些 width/height 属性不是百分比。这是一个longstanding bug.
您需要编辑 icon.svg 文件,使其符合上述条件。
如前所述,这是一个开放性错误,由 Firefox 在绘制 canvas 时接受的 SVG 尺寸规范的限制引起。有一个解决方法。
Firefox 要求 SVG 本身具有明确的宽度和高度属性。我们可以通过将 SVG 获取为 XML 并修改它来添加这些。
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 时的错误处理。 this inline-svg handler(大约第 110 行)中演示了更好的错误处理,我从中派生了此方法的一部分。
这不是最可靠的解决方案,但这个 hack 对我们的目的有用。提取 viewBox
数据并将这些维度用于 width/height 属性。
这仅在第一个 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;
}
}