通过 JavaScript 访问 SVG 数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5201782/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 16:21:31  来源:igfitidea点击:

Accessing the SVG data through JavaScript

javascripthtmlcanvassvg

提问by Zain

I am working on a web application using SVG & JS. Things are going well but I am a bit confused by data handling.

我正在使用 SVG 和 JS 开发一个 Web 应用程序。事情进展顺利,但我对数据处理有点困惑。

The data for objects that users create is dynamically appended to the SVG root. Where & how is this SVG data stored, & how can I access it? The reason I want to read it is that I want to convert this SVG data into HTML5 canvas (using Google's canvg) and finally turn that into a png image to be stored in our db for reference.

用户创建的对象的数据动态附加到 SVG 根。此 SVG 数据存储在何处以及如何存储,以及如何访问它?我想阅读它的原因是我想将此 SVG 数据转换为 HTML5 画布(使用 Google 的canvg),最后将其转换为 png 图像以存储在我们的数据库中以供参考。

Any guidance would be much appreciated.

任何指导将不胜感激。

回答by Phrogz

When a web browser loads an SVG file, it (roughly) parses the stream of bytes and creates a DOM structure in memoryfor the SVG file. It is this in-memory representation that is modified when script dynamically creates and appends new elements to the document.

当 Web 浏览器加载 SVG 文件时,它(粗略地)解析字节流并在内存中为 SVG 文件创建 DOM 结构。当脚本动态创建新元素并将新元素附加到文档时,会修改这种内存中的表示。

You can see a very simple example of this here:
http://phrogz.net/svg/svg_in_xhtml5.xhtml
The SVG file loads with a circle in the background for the face, and then JavaScript dynamically creates the eyes and nose and appends them as children of the SVG element.

你可以在这里看到一个非常简单的例子:
http: //phrogz.net/svg/svg_in_xhtml5.xhtml
SVG 文件在背景中加载一个圆圈作为脸部,然后 JavaScript 动态创建眼睛和鼻子并附加它们作为 SVG 元素的子元素。

There are three ways I know of that you can access this information and get it into a canvas:

我知道您可以通过三种方式访问​​此信息并将其放入画布中:

  1. You can walk the DOM of elements yourself, and issue canvas context drawing commands:

    var svg = document.getElementsByTagName('svg')[0];
    var kids = svg.childNodes;
    for (var i=0,len=kids.length;i<len;++i){
      var kid = kids[i];
      if (kid.nodeType!=1) continue; // skip anything that isn't an element
      switch(kid.nodeName){
        case 'circle':
          // ...
        break;
        case 'path':
          // ...
        break;
        // ...
      }
    }
    

    See the SVG DOM referencefor more information on the properties and methods available to you, or just use the DOM 2 Coremethods to fetch properties. For simplicity, you might want to use the latter.

    For example, you can either access the current (non-SMIL animated) cxof the circle using the SVG DOM via var cx = kid.cx.baseVal.value;or you can more simply do var cx = kid.getAttribute('cx');.

    This is going to be simple where SVG and Canvas have similar commands (e.g. rector line), a little bit of work where they don't match exactly (e.g. circleversus arcTo, polylineas a series of lineTocommands), and a lot of work for the pathelement and its many drawing commands.

  2. Per this answerand this paper, use the XMLSerializerinterface to get the SVG back as markup, use that directly as the source of an img, and then drawImagethat to the canvas. Using my above example:

    var svg_xml = (new XMLSerializer).serializeToString(svg);
    console.log(svg_xml);
    // "<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" viewBox="-350 -250 700 500">
    // <circle r="200" class="face" fill="red"/>
    // <path fill="none" class="face" transform="translate(-396,-230)" d="M487.41,282.411c-15.07,36.137-50.735,61.537-92.333,61.537         c-41.421,0-76.961-25.185-92.142-61.076"/>
    // <circle cx="-60" cy="-50" r="20" fill="#000"/>
    // <circle cx="60" cy="-50" r="20" fill="#000"/>
    // </svg>"
    var ctx = myCanvas.getContext('2d');
    var img = new Image;
    img.onload = function(){ ctx.drawImage(img,0,0); };
    img.src = "data:image/svg+xml;base64,"+btoa(svg_xml);
    

    If you have SVG in XHTML (as in my example), the serialization will not capture styles defined outside of the SVG block (as in my example) and so they will not be applied in the image.

    Note that per this question/answer(mine), you must set the onloadattribute beforeyou set the srcto the data URL if you want IE9 compatibility.

    Unfortunately, due to what may or may not be a bug, drawing a data-url image to a canvas causes the origin-clean flag to be set to false, which means that you will not be able to call toDataURL()on the canvas and get your PNG back. So...

  3. I suppose for your specific needs of Client-modified SVG->Canvas->PNG-on-server you will need to use the first step above to serialize the svg_xmland then pass that raw source to canvg.

  1. 您可以自己遍历元素的 DOM,并发出画布上下文绘制命令:

    var svg = document.getElementsByTagName('svg')[0];
    var kids = svg.childNodes;
    for (var i=0,len=kids.length;i<len;++i){
      var kid = kids[i];
      if (kid.nodeType!=1) continue; // skip anything that isn't an element
      switch(kid.nodeName){
        case 'circle':
          // ...
        break;
        case 'path':
          // ...
        break;
        // ...
      }
    }
    

    有关可用的属性和方法的更多信息,请参阅SVG DOM 参考,或者仅使用DOM 2 Core方法来获取属性。为简单起见,您可能希望使用后者。

    例如,您可以cx使用 SVG DOM 通过访问当前(非 SMIL 动画)圆圈,var cx = kid.cx.baseVal.value;或者您可以更简单地执行var cx = kid.getAttribute('cx');.

    这将很简单,其中 SVG 和 Canvas 具有相似的命令(例如rectline),在它们不完全匹配的地方做一些工作(例如circlevs arcTopolyline作为一系列lineTo命令),以及path元素和它的许多绘图命令

  2. 根据this answerthis paper,使用XMLSerializer界面将SVG作为标记返回,直接将其用作 an 的来源img,然后drawImage将其用于画布。使用我上面的例子:

    var svg_xml = (new XMLSerializer).serializeToString(svg);
    console.log(svg_xml);
    // "<svg xmlns="http://www.w3.org/2000/svg" version="1.1" baseProfile="full" viewBox="-350 -250 700 500">
    // <circle r="200" class="face" fill="red"/>
    // <path fill="none" class="face" transform="translate(-396,-230)" d="M487.41,282.411c-15.07,36.137-50.735,61.537-92.333,61.537         c-41.421,0-76.961-25.185-92.142-61.076"/>
    // <circle cx="-60" cy="-50" r="20" fill="#000"/>
    // <circle cx="60" cy="-50" r="20" fill="#000"/>
    // </svg>"
    var ctx = myCanvas.getContext('2d');
    var img = new Image;
    img.onload = function(){ ctx.drawImage(img,0,0); };
    img.src = "data:image/svg+xml;base64,"+btoa(svg_xml);
    

    如果您在 XHTML 中有 SVG(如我的示例),序列化将不会捕获在 SVG 块之外定义的样式(如我的示例),因此它们不会应用于图像。

    请注意,根据此问题/答案(我的),如果您想要 IE9 兼容性,则必须将 设置为数据 URL之前设置该onload属性。src

    不幸的是,由于可能是也可能不是bug,将 data-url 图像绘制到画布会导致 origin-clean 标志设置为false,这意味着您将无法toDataURL()在画布上调用并获取 PNG背部。所以...

  3. 我想对于客户端修改的 SVG->Canvas->PNG-on-server 的特定需求,您需要使用上面的第一步来序列化svg_xml,然后将该原始源传递给canvg

Alternatively, you might consider serializing the SVG per the above and submitting that directly to your server and doing server-side SVG-to-PNG conversion.

或者,您可以考虑按照上述方法序列化 SVG 并将其直接提交到您的服务器并进行服务器端 SVG-to-PNG 转换

回答by Nicolas78

The webkit bug mentioned in @Phrogz answer seems to have been fixed in more recent versions, so I found a solution that doesn't require a manual parse is

@Phrogz 回答中提到的 webkit 错误似乎已在更新的版本中得到修复,因此我找到了一个不需要手动解析的解决方案

// from http://bl.ocks.org/biovisualize/1209499
// via https://groups.google.com/forum/?fromgroups=#!topic/d3-js/RnORDkLeS-Q
var xml = d3.select("svg")
  .attr("title", "test2")
  .attr("version", 1.1)
  .attr("xmlns", "http://www.w3.org/2000/svg")
  .node().parentNode.innerHTML;

// from http://webcache.googleusercontent.com/search?q=cache:http://phpepe.com/2012/07/export-raphael-graphic-to-image.html
// via http://stackoverflow.com/questions/4216927/problem-saving-as-png-a-svg-generated-by-raphael-js-in-a-canvas
var canvas = document.getElementById("myCanvas")
canvg(canvas, svgfix(xml));
document.location.href = canvas.toDataURL();

which requires https://code.google.com/p/svgfix/and https://code.google.com/p/canvg/and uses d3.js to obtain the svg source, which should be doable without d3.js as well (but this takes care of adding required metadata which can lead to problems when missing).

这需要 https://code.google.com/p/svgfix/https://code.google.com/p/canvg/并使用 d3.js 来获取 svg 源,这在没有 d3.js 的情况下应该是可行的以及(但这需要添加所需的元数据,这可能会导致丢失时出现问题)。