如何将DOM中的内联SVG绘制到画布中?

32

我需要转换一个 .svg 文件/图像为一个 .png 文件/图像...

我在页面上展示一个 .svg 图像,它保存在我的服务器上(以 .png 文件的形式)。我需要按需求将其转换为 .png 文件(当点击按钮时),并将 .png 文件保存在服务器上(我将使用 .ajax 请求进行此操作)。但是问题在于转换。

我阅读了许多有关 HTML5 Canvas 的内容,这可能可以帮助我完成现在需要做的事情,但我找不到任何明确解决方案来解决我的问题,并且,说实话,我没有理解我找到的所有内容...因此,我需要关于如何操作的明确建议/帮助。

下面是“html idea”模板:

<html>
    <body>
        <svg id="mySvg" width="300px" height="300px">
            <!-- my svg data -->
        </svg>
        <label id="button">Click to convert</label>
        <canvas id="myCanvas"></canvas>
    </body>
</html>

还有一些 JavaScript 代码:

<script>
    $("body").on("click","#button",function(){
        var svgText = $("#myViewer").outerHTML;
        var myCanvas = document.getElementById("canvas");
        var ctxt = myCanvas.getContext("2d");
    });
</script>

然后,我需要将SVG绘制到画布中,取回Base64数据,并将其保存在我的服务器上的.png文件中...但是...怎么做呢?我了解了许多不同的解决方案,但实际上...我感到迷茫...我正在一个jsfiddle上工作,但实际上...我无处可去...http://jsfiddle.net/xfh7nctk/6/ ... 谢谢阅读/帮助


一个 .svg 图像[...]被保存为 .png 文件[...], 这是如何实现的?! - Biffen
请查看https://dev59.com/EG865IYBdhLWcg3wivGi#3769883。 - Prusse
请查看我在此处提供的答案,它应该可以解决你的问题。 - Cels
2个回答

48

对于内联SVG,您需要:

  • 将SVG字符串转换为Blob
  • 获取Blob的URL
  • 创建一个图像元素并将URL设置为src
  • 加载完成(onload)后,您可以将SVG作为图像绘制在画布上
  • 使用toDataURL()从画布中获取PNG文件。

例如:

function drawInlineSVG(ctx, rawSVG, callback) {

    var svg = new Blob([rawSVG], {type:"image/svg+xml;charset=utf-8"}),
        domURL = self.URL || self.webkitURL || self,
        url = domURL.createObjectURL(svg),
        img = new Image;

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

    img.src = url;
}

// usage:
drawInlineSVG(ctxt, svgText, function() {
    console.log(canvas.toDataURL());  // -> PNG data-uri
});

当然,这里的console.log只是举个例子。在这里存储/传输字符串。 (我还建议在方法内添加一个onerror处理程序)。

还要记得使用属性设置画布大小,可以使用widthheight属性或从JavaScript中设置。


这在这里和JSFiddle上是完美的... 但是当我尝试在我的项目中使用它时,似乎绘图函数不起作用... - Julo0sS
@Julo0sS 你在控制台中得到了什么错误?你是否添加了 onerror 处理程序?尝试在各个阶段插入 console.log 并查看代码停止的位置。输出变量以查看它们是否实际包含数据或未定义。检查是否正确获取了 SVG 的 outerHTML。这是我的意见。 - user1693593
self.URL || self.webkitURL || self 这个语法的含义是什么?你能解释一下这个表达式的作用吗? - user305883
3
如何在 console.log(canvas.toDataURL()); 中获取 canvas - A J
1
这个答案的问题是只有一部分生成的图像被转换为PNG格式,而不是整个SVG图像被转换。 - Yousef
显示剩余2条评论

42

由于从这个问题中提出了其他问题,所以我来得比较晚,因为接受的答案可能会产生不良行为。

@K3N的解决方案几乎是正确的,但我反对使用svgElement.outerHTML
相反,应该优先考虑new XMLSerializer().serializeToString(svgElement)

此外,不必要使用blob和URL API,一个简单的dataURI在浏览器间具有更好的兼容性。

因此,完整的版本如下:

function drawInlineSVG(svgElement, ctx, callback) {
  var svgURL = new XMLSerializer().serializeToString(svgElement);
  var img = new Image();
  img.onload = function() {
    ctx.drawImage(this, 0, 0);
    callback();
  }
  img.src = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgURL);
}

// usage:
drawInlineSVG(document.querySelector('svg'), ctxt, function() {
  console.log(canvas.toDataURL())
});

2
我尝试了之前的答案,如果SVG没有'xmln'属性,它将失败。@Kaiido的答案是一个很好的改进,也可以处理“格式不正确”的SVG。 - user305883
2
@user305883,感谢您的反馈。只想让您知道,当将<svg>元素添加到HTML文档中时,它不必具有xmlns声明。因此,您的SVG并非“格式错误”,而只是一个svg元素,而不是svg文档。 dataURI应该表示一个文档,因此会导致失败。 XMLSerializer()确实会为我们处理转换。 - Kaiido
1
嘿,大家好 - 很抱歉在已经回答的问题上再次提出这个问题。我完全按照这个答案操作,一切都运行良好,除了当我到达 canvas.toDataURL() 时,我遇到了一个 SecurityError 错误。我正在使用这种方法将 Highcharts.js 的 svg 转换为 png,所以我不确定 CORS 的问题从哪里产生(根据我所读的,这是一个 CORS 问题??)Highcharts 的 svg 在浏览器中全部渲染。 - Martin O Leary
2
@TanyaGupta,请注意根节点<svg>必须绝对设置widthheight属性(不能使用相对单位如%);请注意IE没有在指向SVG文档的HTMLImages上设置widthheight(也没有它们的naturalXXX等效项):对于这个浏览器,您需要自己获取宽度,可以从SVG标记中获取,也可以从文档节点的offsetXXX中获取;请注意所有IE < Edge在通过drawImage将SVG绘制到画布时都会污染画布;当在其上绘制<foreignObject>元素时,Safari >= 9会污染画布。 - Kaiido
1
似乎序列化程序无法通过<use>处理引用,因此在生成的“独立”文档(svgURL)中该引用无效。有什么建议吗? - Victoria
显示剩余16条评论

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