JavaScript 将 SVG 下载为 PNG 格式的修改方案

作者:青柠

时间:2026-10-22

浏览:443

本文介绍如何基于 saveSvgAsPng 库修改代码,实现指定 SVG 元素下载为 PNG 格式。通过封装 download 方法,支持传入 DOM 元素或 D3 选择器获取的目标。重点解决了 SVG 未设置命名空间的问题,确保克隆元素正确添加 xmlns 属性而不影响原图,适用于前端图片导出场景。

文章目录

SVG 下载为 PNG 的解决方案

在处理需要将 SVG 图形下载为图片的需求时,网络上常见的解决方案如 https://github.com/exupero/saveSvgAsPng 往往存在缺陷,例如会意外下载页面中所有的 SVG 元素。为了解决这一问题,我在其源码基础上进行了针对性修改,具体实现代码如下所示:


var doctype = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [<!ENTITY nbsp " ">]>'; 
function convertToPng(src, w, h) { 
      var canvas = document.createElement('canvas'); 
      var context = canvas.getContext('2d'); 
      canvas.width = w; 
      canvas.height = h; 
      context.drawImage(src, 0, 0); 
      var png; 
      try { 
        png = canvas.toDataURL("image/png"); 
      } catch (e) { 
        if ((typeof SecurityError !== 'undefined' && e instanceof SecurityError) || e.name == "SecurityError") { 
          console.error("Rendered SVG images cannot be downloaded in this browser."); 
          return; 
        } else { 
          throw e; 
        } 
      } 
      return png; 
    } 
    function isElement(obj) { 
      return obj instanceof HTMLElement || obj instanceof SVGElement; 
    } 
    function reEncode(data) { 
      data = encodeURIComponent(data); 
      data = data.replace(/%([0-9A-F]{2})/g, function (match, p1) { 
        var c = String.fromCharCode('0x' + p1); 
        return c === '%' ? '%25' : c; 
      }); 
      return decodeURIComponent(data); 
    } 
    function uriToBlob(uri) { 
      var byteString = window.atob(uri.split(',')[1]); 
      var mimeString = uri.split(',')[0].split(':')[1].split(';')[0] 
      var buffer = new ArrayBuffer(byteString.length); 
      var intArray = new Uint8Array(buffer); 
      for (var i = 0; i < byteString.length; i++) { 
        intArray[i] = byteString.charCodeAt(i); 
      } 
      return new Blob([buffer], {type: mimeString}); 
    } 
    function downLoad(el, name) { 
      if (!isElement(el)) { 
        throw new Error('an HTMLElement or SVGElement is required; got ' + el); 
      } 
      if (!name) { 
        console.error("文件名为空!"); 
        return; 
      } 
      var xmlns = "http://www.w3.org/2000/xmlns/"; 
      var clone = el.cloneNode(true); 
      clone.setAttribute("version", "1.1"); 
      if (!clone.getAttribute('xmlns')) { 
        clone.setAttributeNS(xmlns, "xmlns", "http://www.w3.org/2000/svg"); 
      } 
      if (!clone.getAttribute('xmlns:xlink')) { 
        clone.setAttributeNS(xmlns, "xmlns:xlink", "http://www.w3.org/1999/xlink"); 
      } 
      var svg = clone.outerHTML; 
      var uri = 'data:image/svg+xml;base64,' + window.btoa(reEncode(doctype + svg)); 
      var image = new Image(); 
      image.onload = function () { 
        var png = convertToPng(image, image.width, image.height); 
        var saveLink = document.createElement('a'); 
        var downloadSupported = 'download' in saveLink; 
        if (downloadSupported) { 
          saveLink.download = name + ".png"; 
          saveLink.style.display = 'none'; 
          document.body.appendChild(saveLink); 
          try { 
            var blob = uriToBlob(png); 
            var url = URL.createObjectURL(blob); 
            saveLink.href = url; 
            saveLink.onclick = function () { 
              requestAnimationFrame(function () { 
                URL.revokeObjectURL(url); 
              }) 
            }; 
          } catch (e) { 
            saveLink.href = uri; 
          } 
          saveLink.click(); 
          document.body.removeChild(saveLink); 
        } 
      }; 
      image.src = uri; 
    } 

download 方法的使用与参数说明

实际使用时,只需调用封装好的 download 方法即可,建议将其封装为独立的下载服务模块。该方法的第一个参数 el 代表目标 DOM 元素,即需要下载的 SVG 节点。获取该元素的方式灵活,既可以使用原生 JavaScript,也可以通过 D3 等库的选择器来获取。例如:


<div id="svg-parent-div"><svg>...</svg></div> 

通常可以通过获取 SVG 的父容器元素来定位目标节点,具体操作如:


var el = d3.select("#svg-parent-div").node().children[0]; 
download(el,"下载图片"); 

完成上述步骤后,即可正常触发下载。

命名空间处理机制

代码中特别处理了 SVG 元素未设置命名空间的情况。如果检测到 SVG 元素缺少必要的命名空间定义,程序会自动进行添加,相关逻辑如下:


el.setAttributeNS(xmlns, "xmlns", "http://www.w3.org/2000/svg"); 
el.setAttributeNS(xmlns, "xmlns:xlink", "http://www.w3.org/1999/xlink"); 

需要注意的是,上述添加命名空间的逻辑仅作用于克隆后的元素副本,因此不会对页面上原始的 SVG 元素产生任何副作用。

总结

以上介绍了利用 JavaScript 将 SVG 图片下载为 PNG 格式的具体实现方法,希望能为遇到类似问题的开发者提供参考。如有疑问欢迎留言,我们将及时回复。感谢大家的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章