如何通过Canvas将SVG下载为PNG时避免黑色或空白图像?

5

我正在尝试使用Canvas将SVG图像下载为PNG。

我的具体步骤如下:

  1. 获取SVG HTML;
  2. 将SVG转换为Canvas;
  3. 将Canvas下载为PNG。

以下是我的代码:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var data = document.getElementById("mySvg");
var elementWidth = data.clientWidth || data.parentNode.clientWidth;
var elementHeight = data.clientHeight || data.parentNode.clientHeight;
var DOMURL = window.URL || window.webkitURL || window;
var aLines = document.createElement("a");
var img = new Image();
var svg = new Blob([data.outerHTML], {
  type: 'image/svg+xml'
});
var url = DOMURL.createObjectURL(svg);

img.onload = function() {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
}

img.src = url;

aLines.href = canvas.toDataURL();
aLines.download = "test.png";

aLines.click();
<!-- Learn about this code on MDN: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas -->

<canvas id="canvas" style="border:2px solid black;" width="200" height="200">
</canvas>
<svg id="mySvg" xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  <foreignObject width="100%" height="100%">
    <div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">
      <em>I</em> like
      <span style="color:white; text-shadow:0 0 2px blue;">
             cheese</span>
    </div>
  </foreignObject>
</svg>

当我执行这段代码时,没有得到SVG内容的图像,而是一个黑色的图像。
我尝试将画布背景改成白色,但是结果得到了一个空白的图像,没有SVG内容。
你有什么建议来解决这个问题或者可以指点我正确的方向吗?
谢谢。

在调用canvas.toDataURL之前,您必须等待图像被绘制。这将在“img.onload”事件处理程序中异步发生。所有在此处理程序之后的代码都应该移动到处理程序内部,否则它将在处理程序之前执行。 - Kaiido
2
@Kaiido 我尝试了你的建议,但是我得到了“Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.”的错误。我尝试通过设置“img.crossOrigin='Anonymous'”和“img.crossOrigin='use-credentials'”来修复它。但是这些都没有起作用...你有其他的建议吗?这是修改后的fiddle链接:https://jsfiddle.net/m_oliv/fjdtr9vp/ - m-oliv
1个回答

2

我曾经遇到过相同的问题,我的代码和你的类似。然而,当我修改成以下代码后,问题得以解决:

const saveSvgAsPng = (svgElt, imageName) => {
    const xml = new XMLSerializer().serializeToString(svgElt);
    const svg64 = btoa(xml);
    const b64Start = 'data:image/svg+xml;base64,';
    const { width, height } = svgElt.getBoundingClientRect();

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = width;
    canvas.height = height;
    ctx.fillStyle = 'white'; // background color for the canvas
    ctx.fillRect(0, 0, width, height); // fill the color on the canvas

    const image = new Image();
    image.onload = () => {
        ctx.drawImage(image, 0, 0, width, height);

        downloadImage(canvas, imageName); // download the image when the image has been loaded onto the canvas
    };
    image.src = b64Start + svg64;
};

const downloadImage = (canvas, imageName) => {
    canvas.toBlob((blob) => {
        const anchor = document.createElement('a');
        anchor.download = imageName;
        anchor.href = URL.createObjectURL(blob);

        anchor.click(); 

        URL.revokeObjectURL(anchor.href); // remove it from memory and save on memory!
    }, 'image/png');
};

你可以将SVG元素data与图片名称一起传递给函数。在我的情况下,我创建了一个新的画布(canvas),但是如果你修改函数使用现有的画布,该函数仍然可以工作。
参考:https://www.digitalocean.com/community/tutorials/js-canvas-toblob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接