SVG 超赞,但是有的时候你想把它转换为其他的图片格式,比如 jpg 或 png。如果你用 d3.js 创建了一些图表,你可以提供把这些图表保存为图片格式的选项。网上晃了一圈,我最后找到了这篇文章《Save SVG as PNG》。虽然它提供了一种解决问题的还不错的方案,但我还是想把它扩展一下,以提供更多的可选方案。

    最开始,我想举个栗子,你用 D3 创建了一些图,然后想用一个 button 和一个 js 函数来实现 svg 到图片的格式转换。我想你有三种选择来实现这个展示图片的需求:一,直接用 svg 作为 dataurl 的 img 标签,这当然还是一个技术上的 svg,没啥区别;二,用 html5 canvas 把 svg 转换为 png,然后插入一个 base64 data 到一个 img 标签;三,把 svg 转换为 png 的二进制数据形式,把数据加载到一个 blob url,这样你可以在一个 img 标签内提供一个真实的图片文件。

    让我们从一个d3 的例子开始。我稍微改了一下代码,把 svg 的内容放到一个 div 而不是 body 里面,我还加了 “保存图片” 的按钮,以及前面提到的 js 函数。你可以看这里演示。下面是这个实现的关键代码:

    1. d3.select("#save").on("click", function(){
    2. var html = d3.select("svg")
    3. .attr("version", 1.1)
    4. .attr("xmlns", "http://www.w3.org/2000/svg")
    5. .node().parentNode.innerHTML;
    6. //console.log(html);
    7. var imgsrc = 'data:image/svg+xml;base64,'+ btoa(html);
    8. var img = '<img src="'+imgsrc+'">';
    9. d3.select("#svgdataurl").html(img);
    10. });

    我们抓取 d3 生成的 svg,把它转换为 base64 的 dataurl 形式,把它黏贴到一个 img 标签。这个时候它还是一个 svg,我们把它加载到一个真实的图片里面,再把这个图片加载到 canvas 对象中。这个时候,我们可以从 canvas 抓取 png 的 dataurl,并且把它插入到页面中的 img 标签上。

    现在我们有了一个带 base64 加密的图片数据资源的真实的 png 图片了。再用HTML5 link download attribute,我们就可以以编程的方式强制浏览器下载这个图片。这里就是最终的实现。来看看核心代码:

    1. d3.select("#save").on("click", function(){
    2. var html = d3.select("svg")
    3. .attr("version", 1.1)
    4. .attr("xmlns", "http://www.w3.org/2000/svg")
    5. .node().parentNode.innerHTML;
    6. var imgsrc = 'data:image/svg+xml;base64,'+ btoa(html);
    7. var img = '<img src="'+imgsrc+'">';
    8. d3.select("#svgdataurl").html(img);
    9. // 上面和前面的实现是一样的
    10. var canvas = document.querySelector("canvas"),
    11. context = canvas.getContext("2d");
    12. var image = new Image;
    13. image.src = imgsrc;
    14. image.onload = function() {
    15. context.drawImage(image, 0, 0);
    16. var canvasdata = canvas.toDataURL("image/png");
    17. var pngimg = '<img src="'+canvasdata+'">';
    18. d3.select("#pngdataurl").html(pngimg);
    19. var a = document.createElement("a");
    20. a.download = "sample.png";
    21. a.href = canvasdata;
    22. document.body.appendChild(a);
    23. a.click();
    24. };
    25. });

    现在,这个方案已经够酷了,特别是你能通过编程的形式强制下载图片文件。但是,怎么实现在客户端创建一个真实的图片呢?(译者按:意思是说不需要经过服务端,用户提供一个 svg,直接导出一个 png。)我们可以用一个 blob 来保存图片数据,然后把它丢到一个 blob url。我以前在我的文章Drag and Drop Image Upload to ImgurMaking Video Screenshots with HTML5中搞定过 blobs 转换,但是那段代码必须使用 html5 的 FileReader 的相关功能,但现在我们只需要把图片数据转换到一个atobDataView的二进制格式中。 这里是我实现的最终效果。

    最后,让我们完成一个拖拽放入 SVG 生成 PNG 图片转换器,你可以查看这个页面的源代码来查看它是怎样实现的。

    其他参考:

    • Save SVG as PNG
    • Canvg
    • SVG Crowbar – Chrome 特殊标签栏,它能够从 html 文档中提取 svg 元素并且包含它的所有样式,然后下载它们保存为一个 svg 图片。

    注:a 标签的 download 属性支持情况(来自 MDN):

    Feature Chrome Edge Firefox (Gecko) Internet Explorer Opera Safari
    download 14 (Yes) 20.0 (20.0) Edge 13 15 10.1

    另外,这是我第一次完整的翻译一篇文章,不求翻译精准,但求读者能够准确读懂。如果你觉得这篇文章有用,请为你的收获打赏,让我坚持翻译更多好文章。
    https://juejin.im/entry/6844903478490300430