【问题标题】:(Embed and) Refer to an external SVG via D3 and/or javascript(嵌入和)通过 D3 和/或 javascript 引用外部 SVG
【发布时间】:2014-01-18 20:36:08
【问题描述】:

我有一个 .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>

【问题讨论】:

    标签: javascript xml svg d3.js


    【解决方案1】:

    有一个更好的解决方案。

    我没有意识到d3.xml() 函数将读入的 xml 文件作为document fragment 返回(而不是将其转换为 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 添加到 &lt;div&gt; 或任何其他可以包含它的元素:http://jsfiddle.net/J8sp3/3/

    此方法应适用于任何支持 SVG 的浏览器。我什至在IE8中试过,即使IE8不知道怎么画圆和矩形,DOM结构也是正确的!

    @Seb,请在选择此答案作为已接受答案时取消选择上一个答案。

    【讨论】:

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

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

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

    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 内容作为嵌套 SVG 添加到已使用 D3 创建的 SVG 中。

    正如 Seb 发现的那样,此方法在最新的 Safari 和 Opera 浏览器或 Chrome 31 及更低版本上不起作用

    问题在于 webkit 浏览器实现 innerHTML 函数的方式,该函数由 d3 的 selection.html() 函数调用。具体来说,the innerHTML function isn't implemented at all on SVG elements!

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

    【讨论】:

    • 谢谢你。也许我不明白,但我在 DOM 中找不到 svg。这是一个小提琴:jsfiddle.net/J8sp3
    • @SebWaynee 读入的 SVG 就在 DOM 中。但是,它也覆盖了您添加到主 SVG 的矩形;请记住,当文件准备好时,d3.text 中的回调函数被调用异步,而不是按照脚本中写入的顺序。添加 SVG 后需要发生的所有事情都必须等到该函数调用它。更新小提琴:jsfiddle.net/J8sp3/1
    • 你是用firefox打开的吗?在Firefox中它可以工作。当我用 Chrome 或 Safari 打开它时,它似乎不起作用。
    • 有人知道如何解决这个问题吗? (我的版本)Chrome/Safari 直接加载时可以显示外部 svg,为什么不能在 read-in-version 中显示?
    • 我创建的小提琴在 Chrome 中工作。如果您在个人计算机上测试代码,则可能是安全限制阻止脚本读取硬盘驱动器上的文件。如果这是问题所在,您将需要运行本地主机 Web 服务器。搜索有关如何设置的信息。
    猜你喜欢
    • 2013-06-20
    • 2017-11-04
    • 2011-04-03
    • 1970-01-01
    • 2013-06-03
    • 2019-02-17
    • 2023-04-08
    • 1970-01-01
    • 2020-01-16
    相关资源
    最近更新 更多