canvas.ToBlob 在 Chrome 工作,但不使用 firefox
canvas.ToBlob working in Chrome, but not working with firefox
Chrome:v96
火狐浏览器:v95
我正在尝试从浏览器将 SVG 图像下载为 PNG 图像。这似乎适用于 Chrome,但我正在使用 Firefox 下载空白图像。知道为什么吗?
export function downloadSvgImage(svgElement: HTMLElement, name: string) {
const xml = new XMLSerializer().serializeToString(svgElement);
const svg64 = window.btoa(xml);
const b64Start = 'data:image/svg+xml;base64,';
const viewBox = svgElement.getAttribute('viewBox');
const dimensionArr = viewBox.split(' ');
const width = parseInt(dimensionArr[2]);
const height = parseInt(dimensionArr[3]);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
const image = new Image();
image.onload = () => {
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
canvas.toBlob((blob: any) => {
const anchor = document.createElement('a');
anchor.download = `${name}.png`;
anchor.href = URL.createObjectURL(blob);
anchor.click();
URL.revokeObjectURL(blob);
}, 'image/png');
};
image.src = b64Start + svg64;
}
我将捕获尺寸的代码更新为以下内容,它按预期通过 Firefox 下载 SVG 图像:
...
let dimensionX = svgElement.viewBox.baseVal.width;
let dimensionY = svgElement.viewBox.baseVal.height;
if (dimensionX == 0 || dimensionY == 0) {
dimensionX = svgElement.getBBox().width;
dimensionY = svgElement.getBBox().height;
}
const width = dimensionX;
const height = dimensionY;
...
function downloadSvgImage(svgElement, name) {
const xml = new XMLSerializer().serializeToString(svgElement);
const svg64 = window.btoa(xml);
const b64Start = "data:image/svg+xml;base64,";
let dimensionX = svgElement.viewBox.baseVal.width;
let dimensionY = svgElement.viewBox.baseVal.height;
if (dimensionX == 0 || dimensionY == 0) {
dimensionX = svgElement.getBBox().width;
dimensionY = svgElement.getBBox().height;
}
const width = svgElement.clientWidth * 0.5; // dimensionX;
const height = svgElement.clientHeight * 0.5; // dimensionY;
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const image = new Image();
image.onload = () => {
canvas.getContext("2d").drawImage(image, 0, 0, width, height);
const url = canvas.toDataURL("image/png", 1);
const anchor = document.createElement("a");
anchor.download = `${name}.png`;
anchor.href = url;
anchor.click();
setTimeout(() => URL.revokeObjectURL(url), 0);
};
image.src = b64Start + svg64;
}
window.onload = function(){
const svg = document.querySelector("#cartman");
svg.setAttribute("width", svg.clientWidth);
svg.setAttribute("height", svg.clientHeight);
console.log("Download starting in 3 seconds...");
setTimeout(() => downloadSvgImage(svg, "cartman-sp"), 3000);
}
<svg xmlns="http://www.w3.org/2000/svg" id="cartman" viewBox="0 0 104 97">
<path d="M14,85l3,9h72c0,0,5-9,4-10c-2-2-79,0-79,1" fill="#7C4E32"/>
<path d="M19,47c0,0-9,7-13,14c-5,6,3,7,3,7l1,14c0,0,10,8,23,8c14,0,26,1,28,0c2-1,9-2,9-4c1-1,27,1,27-9c0-10,7-20-11-29c-17-9-67-1-67-1" fill="#E30000"/>
<path d="M17,32c-3,48,80,43,71-3 l-35-15" fill="#FFE1C4"/>
<path d="M17,32c9-36,61-32,71-3c-20-9-40-9-71,3" fill="#8ED8F8"/>
<path d="M54,35a10 8 60 1 1 0,0.1zM37,38a10 8 -60 1 1 0,0.1z" fill="#FFF"/>
<path d="M41,6c1-1,4-3,8-3c3-0,9-1,14,3l-1,2h-2h-2c0,0-3,1-5,0c-2-1-1-1-1-1l-3,1l-2-1h-1c0,0-1,2-3,2c0,0-2-1-2-3M17,34l0-2c0,0,35-20,71-3v2c0,0-35-17-71,3M5,62c3-2,5-2,8,0c3,2,13,6,8,11c-2,2-6,0-8,0c-1,1-4,2-6,1c-4-3-6-8-2-12M99,59c0,0-9-2-11,4l-3,5c0,1-2,3,3,3c5,0,5,2,7,2c3,0,7-1,7-4c0-4-1-11-3-10" fill="#FFF200"/>
<path d="M56,78v1M55,69v1M55,87v1" stroke="#000" stroke-linecap="round"/>
<path d="M60,36a1 1 0 1 1 0-0.1M49,36a1 1 0 1 1 0-0.1M57,55a2 3 0 1 1 0-0.1M12,94c0,0,20-4,42,0c0,0,27-4,39,0z"/>
<path d="M50,59c0,0,4,3,10,0M56,66l2,12l-2,12M25,50c0,0,10,12,23,12c13,0,24,0,35-15" fill="none" stroke="#000" stroke-width="0.5"/>
</svg>
希望能解决您的问题。
注意:您必须在本地环境中测试此代码,因为从框架下载会被浏览器有意阻止。
更新
所以我意识到我最初的“修复”实际上并没有起作用,因为它仍在下载一个小的空 canvas。我最终发现 Firefox 在 canvas 元素内渲染 SVG 有一个 long-standing bug 除非在 <SVG>
根元素上指定了 width
和 height
属性具有非基于百分比的值。所以我通过手动将它们设置为 <SVG>
的客户维度来解决这个问题:
svg.setAttribute("width", svg.clientWidth);
svg.setAttribute("height", svg.clientHeight);
您可以将 image/png
的维度定义为 <SVG>
的 width & height
的百分比。
const width = svgElement.clientWidth * 0.5; // half the width of the original svg
const height = svgElement.clientHeight * 0.5; // half the height of the original svg
...
canvas.width = width;
canvas.height = height;
我还更新了数据 URL 生成的代码,以使用 canvas 元素的 .toDataURL
方法,并将 revokeObjectURL
延迟到下载后的片刻初始化:
canvas.getContext("2d").drawImage(image, 0, 0, width, height);
const url = canvas.toDataURL("image/png", 1);
const anchor = document.createElement("a");
anchor.download = `${name}.png`;
anchor.href = url;
anchor.click();
setTimeout(() => URL.revokeObjectURL(url), 0);
我希望这次更新能正式解决这个问题。
Chrome:v96 火狐浏览器:v95
我正在尝试从浏览器将 SVG 图像下载为 PNG 图像。这似乎适用于 Chrome,但我正在使用 Firefox 下载空白图像。知道为什么吗?
export function downloadSvgImage(svgElement: HTMLElement, name: string) {
const xml = new XMLSerializer().serializeToString(svgElement);
const svg64 = window.btoa(xml);
const b64Start = 'data:image/svg+xml;base64,';
const viewBox = svgElement.getAttribute('viewBox');
const dimensionArr = viewBox.split(' ');
const width = parseInt(dimensionArr[2]);
const height = parseInt(dimensionArr[3]);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
const image = new Image();
image.onload = () => {
canvas.getContext('2d').drawImage(image, 0, 0, width, height);
canvas.toBlob((blob: any) => {
const anchor = document.createElement('a');
anchor.download = `${name}.png`;
anchor.href = URL.createObjectURL(blob);
anchor.click();
URL.revokeObjectURL(blob);
}, 'image/png');
};
image.src = b64Start + svg64;
}
我将捕获尺寸的代码更新为以下内容,它按预期通过 Firefox 下载 SVG 图像:
...
let dimensionX = svgElement.viewBox.baseVal.width;
let dimensionY = svgElement.viewBox.baseVal.height;
if (dimensionX == 0 || dimensionY == 0) {
dimensionX = svgElement.getBBox().width;
dimensionY = svgElement.getBBox().height;
}
const width = dimensionX;
const height = dimensionY;
...
function downloadSvgImage(svgElement, name) {
const xml = new XMLSerializer().serializeToString(svgElement);
const svg64 = window.btoa(xml);
const b64Start = "data:image/svg+xml;base64,";
let dimensionX = svgElement.viewBox.baseVal.width;
let dimensionY = svgElement.viewBox.baseVal.height;
if (dimensionX == 0 || dimensionY == 0) {
dimensionX = svgElement.getBBox().width;
dimensionY = svgElement.getBBox().height;
}
const width = svgElement.clientWidth * 0.5; // dimensionX;
const height = svgElement.clientHeight * 0.5; // dimensionY;
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const image = new Image();
image.onload = () => {
canvas.getContext("2d").drawImage(image, 0, 0, width, height);
const url = canvas.toDataURL("image/png", 1);
const anchor = document.createElement("a");
anchor.download = `${name}.png`;
anchor.href = url;
anchor.click();
setTimeout(() => URL.revokeObjectURL(url), 0);
};
image.src = b64Start + svg64;
}
window.onload = function(){
const svg = document.querySelector("#cartman");
svg.setAttribute("width", svg.clientWidth);
svg.setAttribute("height", svg.clientHeight);
console.log("Download starting in 3 seconds...");
setTimeout(() => downloadSvgImage(svg, "cartman-sp"), 3000);
}
<svg xmlns="http://www.w3.org/2000/svg" id="cartman" viewBox="0 0 104 97">
<path d="M14,85l3,9h72c0,0,5-9,4-10c-2-2-79,0-79,1" fill="#7C4E32"/>
<path d="M19,47c0,0-9,7-13,14c-5,6,3,7,3,7l1,14c0,0,10,8,23,8c14,0,26,1,28,0c2-1,9-2,9-4c1-1,27,1,27-9c0-10,7-20-11-29c-17-9-67-1-67-1" fill="#E30000"/>
<path d="M17,32c-3,48,80,43,71-3 l-35-15" fill="#FFE1C4"/>
<path d="M17,32c9-36,61-32,71-3c-20-9-40-9-71,3" fill="#8ED8F8"/>
<path d="M54,35a10 8 60 1 1 0,0.1zM37,38a10 8 -60 1 1 0,0.1z" fill="#FFF"/>
<path d="M41,6c1-1,4-3,8-3c3-0,9-1,14,3l-1,2h-2h-2c0,0-3,1-5,0c-2-1-1-1-1-1l-3,1l-2-1h-1c0,0-1,2-3,2c0,0-2-1-2-3M17,34l0-2c0,0,35-20,71-3v2c0,0-35-17-71,3M5,62c3-2,5-2,8,0c3,2,13,6,8,11c-2,2-6,0-8,0c-1,1-4,2-6,1c-4-3-6-8-2-12M99,59c0,0-9-2-11,4l-3,5c0,1-2,3,3,3c5,0,5,2,7,2c3,0,7-1,7-4c0-4-1-11-3-10" fill="#FFF200"/>
<path d="M56,78v1M55,69v1M55,87v1" stroke="#000" stroke-linecap="round"/>
<path d="M60,36a1 1 0 1 1 0-0.1M49,36a1 1 0 1 1 0-0.1M57,55a2 3 0 1 1 0-0.1M12,94c0,0,20-4,42,0c0,0,27-4,39,0z"/>
<path d="M50,59c0,0,4,3,10,0M56,66l2,12l-2,12M25,50c0,0,10,12,23,12c13,0,24,0,35-15" fill="none" stroke="#000" stroke-width="0.5"/>
</svg>
希望能解决您的问题。
注意:您必须在本地环境中测试此代码,因为从框架下载会被浏览器有意阻止。
更新
所以我意识到我最初的“修复”实际上并没有起作用,因为它仍在下载一个小的空 canvas。我最终发现 Firefox 在 canvas 元素内渲染 SVG 有一个 long-standing bug 除非在 <SVG>
根元素上指定了 width
和 height
属性具有非基于百分比的值。所以我通过手动将它们设置为 <SVG>
的客户维度来解决这个问题:
svg.setAttribute("width", svg.clientWidth);
svg.setAttribute("height", svg.clientHeight);
您可以将 image/png
的维度定义为 <SVG>
的 width & height
的百分比。
const width = svgElement.clientWidth * 0.5; // half the width of the original svg
const height = svgElement.clientHeight * 0.5; // half the height of the original svg
...
canvas.width = width;
canvas.height = height;
我还更新了数据 URL 生成的代码,以使用 canvas 元素的 .toDataURL
方法,并将 revokeObjectURL
延迟到下载后的片刻初始化:
canvas.getContext("2d").drawImage(image, 0, 0, width, height);
const url = canvas.toDataURL("image/png", 1);
const anchor = document.createElement("a");
anchor.download = `${name}.png`;
anchor.href = url;
anchor.click();
setTimeout(() => URL.revokeObjectURL(url), 0);
我希望这次更新能正式解决这个问题。