(嵌入和)通过 D3 和/或 javascript 引用外部 SVG

IT技术 javascript xml svg d3.js
2021-03-08 18:33:21

我有一个 .svg 文件,想将它嵌入到我的 d3-graphic 的 svg 结构中。

我还需要通过某些 g 元素的 id 引用附加到 g 元素的所有路径/多边形。

我尝试了不同的方法来嵌入和引用 svg (g's),但由于某些原因它不起作用:

(1) 第一次尝试

// Firefox displays my svg but when i open it with Chrome the svg     
//is not displayed (just default placeholder icon)
// I can't reference the svg-g id's with d3.select functions. 
main_chart_svg
        .append("svg:image")
        .attr("xlink:href", "mySVGpicture.svg")
        .attr("x", "50")
        .attr("y", "50")
        .attr("width", "500")
        .attr("height", "500");  

main_chart_svg.select("#anIdWhichIsInTheSvgFile").remove(); //// This doesn't work

(2) 第二次尝试

// This displays the svg but -not- inside my main-chart-svg. I want to keep the graphic   
//things seperate to html-stuff.
d3.xml("mySVGpicture.svg", "image/svg+xml", function(xml) {
        document.body.appendChild(xml.documentElement);
    });
//----------or----------//
    d3.select("body")
        .append("object")
        .attr("data", "mySVGpicture.svg")
        .attr("width", 500)
        .attr("height", 500)
        .attr("type", "image/svg+xml");

d3.select("#anIdThatIsInTheSvgFile").remove(); //does not work.

(3) svg 文件看起来像这样:

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="400px"
     height="400px" viewBox="0 0 400 400" enable-background="new 0 0 400 400" xml:space="preserve">
    <g id="anIdWhichIsInTheSvgFile">
        <g id="DE">
            <path fill="#FEDCBD" d="M215.958,160.554c0,0-0.082, ... ,1.145
                l0.865,0.656L215.958,160.554z"/>
            <path fill="#FEDCBD" d="M208.682,155.88l1.246,1.031c0,0,0.191,0.283, ... ,0.572L208.682,155.88z"/>
            <polygon fill="#FEDCBD" points="190.76,153.007 190.678, ... ,153.938 
                191.427,152.906"/>
            <polygon fill="#FEDCBD" points="170.088,151.015 169.888,150.067 169.125,150.075 168.846,150.836 169.521,151.588"/>
            <polygon fill="#FEDCBD" points="168.953,152.067 168.188,151.505 168.674,152.639"/>
            <polygon fill="#FEDCBD" points="170.105,153.099 170.666,152.052 170.002,152.248"/>
    </g>
    <g id="anIdThatIsInTheSvgFile">
    ...
    </g>
</svg>
2个回答

有一个更好的解决方案。

我没有意识到该d3.xml()函数将读入的 xml 文件作为文档片段返回(而不是将其转换为 JSON)。换句话说,它返回一个现成的 DOM 树,可以在您需要的任何地方插入主文档的 DOM 中。

知道(并且知道独立的 SVG 文件也是 XML 文件),这可能是将外部 SVG 内容添加到网页的最可靠方法:

d3.xml("http://upload.wikimedia.org/wikipedia/commons/a/a0/Circle_-_black_simple.svg", 
        function(error, documentFragment) {

    if (error) {console.log(error); return;}

    var svgNode = documentFragment
                .getElementsByTagName("svg")[0];
    //use plain Javascript to extract the node

    main_chart_svg.node().appendChild(svgNode);
    //d3's selection.node() returns the DOM node, so we
    //can use plain Javascript to append content

    var innerSVG = main_chart_svg.select("svg");

    innerSVG.transition().duration(1000).delay(1000)
          .select("circle")
          .attr("r", 100);

});

在这里小提琴:http : //jsfiddle.net/J8sp3/4/

请特别注意 (a) 我将内容添加到现有 SVG,以及 (b) 我没有删除该 SVG 的任何当前内容。

当然,您也可以将 SVG 添加到一个<div>或任何其他可以包含它的元素中:http : //jsfiddle.net/J8sp3/3/

这种方法应该适用于任何支持 SVG 的浏览器。我什至在IE8中尝试过,即使IE8不知道如何绘制圆形和矩形,DOM结构也是正确的!

@Seb,请在选择此答案时取消选择先前的答案作为已接受的答案。

@AmeliaBR 你知道这个技巧是否也适用于 nodejs ......?我正在尝试使用 nodejs 操作 svg,但即使使用 d3 加载 dom 也很困难。
2021-04-18 18:33:21
如果我想使用几个不同的独立.svg文件(例如,读取这个黑色圆圈,然后读取一个红色圆圈等),有没有办法循环遍历此代码
2021-04-19 18:33:21
@JPCF 这实际上取决于您在 Node 中运行的其他库,以及它们是否完全取代了 D3 在后台使用的 DOM 方法。
2021-04-20 18:33:21
@Amyunimus:您可以调用多个不同的文件读取命令,并为每个命令传递一个回调以提取相关的文档片段并将其添加到您的主 DOM 中。但是,如果您想设置一个全局回调函数在所有文件下载和解析后运行,您可能应该使用queue.js来处理您的回调。
2021-04-30 18:33:21
随着 d3 v5 的引入,这不再起作用,因为 d3 切换到使用Promise: d3.xml("file.xml").then(function(xml) { console.log(xml); });
2021-05-07 18:33:21

从另一个文件嵌入 SVG 内容的正确格式是使用<use>元素。但是,您随后无法访问嵌套 SVG 的 DOM 结构(即,单个元素)——它们都被视为单个图像。

如果您希望能够修改外部文件描述的图形,最好将外部文件作为文本/XML 文件读取(使用d3.text),然后使用该 XML 文本在 DOM 中创建 SVG 图形将内容编写为容器的内部 HTML<div>

d3.text("mySVGpicture.svg", function(error, externalSVGText) {
         if (error) {console.log(error); return;}

         chart_div.html(externalSVGText);

         svg = chart_div.select("svg");

         do_stuff();
});

你的 DOM 现在应该看起来像

body
  div.chart
     svg
       g#anIDWhichIsInTheSVGFile
         g#DE
           path
           /*etc*/
       g#anotherIDWhichIsInTheSVGFile

您现在可以正常选择元素,并设置您创建的 svg 的位置或大小,或者删除 g 元素或其他任何东西——它们都只是 DOM 中的普通元素。确保您的“id”属性不冲突——它们在整个页面中必须是唯一的。

此处的示例显示了使用 d3 添加到 SVG 的附加内容,以及从外部 SVG 中选择和修改元素的 d3 转换:http :
//jsfiddle.net/J8sp3/2/

警告

此答案的原始版本建议将外部 SVG 内容添加为已使用 D3 创建的 SVG 内的嵌套 SVG。

正如 Seb 发现的那样,此方法不适用于最新的 Safari 和 Opera 浏览器,或 Chrome 31 及以下版本。

问题在于 webkit 浏览器实现该innerHTML函数的方式,该函数由 d3 的selection.html()函数调用。具体来说,innerHTML功能根本没有在 SVG 元素上实现

最令人困惑的是该方法不会返回任何错误,并且某些版本的 Chrome 实际上显示 SVG 内容而不将其添加到 DOM(就像您使用了<use xlink:href="external.svg"/>)。

到目前为止,谢谢你。也许我不明白,但我在 DOM 中找不到 svg。这是一个小提琴:jsfiddle.net/J8sp3
2021-04-28 18:33:21
@SebWaynee 读入的 SVG 就在 DOM 中。但是,它也会覆盖您添加到主 SVG 的矩形;记住回调函数d3.text被调用异步,当文件已准备就绪,而不是在它写在脚本的顺序。添加 SVG 之后需要发生的一切都必须等到该函数调用它。更新小提琴:jsfiddle.net/J8sp3/1
2021-04-29 18:33:21
我创建的小提琴在 Chrome 中工作。如果您在个人计算机上测试代码,则可能是安全限制阻止脚本读取硬盘驱动器上的文件。如果这是问题所在,您将需要运行本地主机 Web 服务器。搜索有关如何设置的信息。
2021-05-02 18:33:21
你是用firefox打开的吗?在 Firefox 中它有效。当我用 Chrome 或 Safari 打开它时,它似乎不起作用。
2021-05-11 18:33:21
有人知道如何解决这个问题吗?(我的版本)Chrome/Safari 在我直接加载时可以显示外部 svg,为什么不在读入版本中显示?
2021-05-11 18:33:21