2

我正在尝试制作一个力图,其中有几个下拉框可以过滤显示的数据。第一个(这是我现在几乎在的地方)检查类型,并且只显示具有与类型匹配的源或目标的节点和链接。

我现在拥有的是选择过滤器的能力,并且图形更新,它删除了不必要的节点,并将剩余的节点重新格式化为正确的。但它只在第一次起作用。如果我“重新过滤”,它就会开始变得混乱。

这是我的完整代码,我对 javascript (&d3) 很陌生,而且我一直在厚颜无耻地从 bl.ocks.org 窃取,所以请随时在“noob”中回答。提前致谢。

另外,我把它放在了一个 jsfiddle 上:http: //jsfiddle.net/J85Vu/

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <title>Enterprise Collaboration Map</title>
    <script type="text/javascript" src="d3.v3.js"></script>
    <script type="text/javascript" src="jquery-1.10.2.min.js"></script>

    <style type="text/css">

        path.link {
          fill: none;
          stroke: #666;
          stroke-width: 1.5px;
        }

        marker#a {
          fill: green;
        }
        path.link.a {
          stroke: green;
        }
        circle.a {
          fill: green;
          stroke: #333;
          stroke-width: 1.5px;
        }
        marker#b {
          fill: blue;
        }
        path.link.b {
          stroke: blue;
        }
        circle.b {
          fill: blue;
          stroke: #333;
          stroke-width: 1.5px;
        }
        marker#c {
          fill: orange;
        }
        path.link.c {
          stroke: orange;
        }
        circle.c {
          fill: orange;
          stroke: #333;
          stroke-width: 1.5px;
        }

        circle {
          fill: #ccc;
          stroke: #333;
          stroke-width: 1.5px;
        }


        text {
          font: 10px sans-serif;
          pointer-events: none;
        }

        text.shadow {
          stroke: #fff;
          stroke-width: 3px;
          stroke-opacity: .8;
        }

    </style>
  </head>
  <body>
    <select class="BU">
        <option value="a">A</option>
        <option value="b">B</option>
        <option value="c">C</option>
    </select>


    <script type="text/javascript">


        var links = [
        {source:"one",target:"two", type:"a", typeKBP:"a"},
        {source:"two",target:"three", type:"a", typeKBP:"a"},
        {source:"three",target:"four", type:"a", typeKBP:"a"},
        {source:"four",target:"five", type:"a", typeKBP:"b"},
        {source:"five",target:"six", type:"b", typeKBP:"b"},
        {source:"six",target:"seven", type:"b", typeKBP:"b"},
        {source:"seven",target:"eight", type:"b", typeKBP:"b"},
        {source:"eight",target:"nine", type:"b", typeKBP:"c"},
        {source:"nine",target:"ten", type:"c", typeKBP:"c"},
        {source:"ten",target:"one", type:"c", typeKBP:"a"},
        {source:"one",target:"three", type:"a", typeKBP:"a"},
        {source:"two",target:"four", type:"a", typeKBP:"a"},
        {source:"three",target:"five", type:"a", typeKBP:"b"},
        {source:"four",target:"six", type:"a", typeKBP:"b"},
        {source:"five",target:"seven", type:"b", typeKBP:"b"},
        {source:"six",target:"eight", type:"b", typeKBP:"b"},
        {source:"seven",target:"nine", type:"b", typeKBP:"c"},
        {source:"eight",target:"ten", type:"b", typeKBP:"c"},
        {source:"nine",target:"one", type:"c", typeKBP:"a"},
        {source:"ten",target:"two", type:"c", typeKBP:"a"},
        {source:"one",target:"four", type:"a", typeKBP:"a"},
        {source:"two",target:"five", type:"a", typeKBP:"b"},
        {source:"three",target:"six", type:"a", typeKBP:"b"},
        {source:"four",target:"seven", type:"a", typeKBP:"b"},
        {source:"five",target:"eight", type:"b", typeKBP:"b"},
        {source:"six",target:"nine", type:"b", typeKBP:"c"},
        {source:"seven",target:"ten", type:"b", typeKBP:"c"},
        {source:"eight",target:"one", type:"b", typeKBP:"a"},
        {source:"nine",target:"two", type:"c", typeKBP:"a"},
        {source:"ten",target:"three", type:"c", typeKBP:"a"}
        ];

        var inputlinks=[];
        var nodes = {};

        inputlinks.push(links);
        // Compute the distinct nodes from the links.
        links.forEach(function(link) {
          link.source = nodes[link.source] || (nodes[link.source] = {name: link.source, type:link.type});
          link.target = nodes[link.target] || (nodes[link.target] = {name: link.target, type:link.typeKBP});
        });

        var w = 1024,
            h = 800;
        //setup initial force layout
        var force = d3.layout.force()
            .gravity(0.4)
            .size([w, h])
            .nodes(d3.values(nodes))
            .links(links)
            .linkDistance(100)
            .charge(-1000)
            .on("tick", tick)
            .start();

        var svg = d3.select("body").append("svg:svg")
            .attr("width", w)
            .attr("height", h);

        // Per-type markers, as they don't inherit styles.
        svg.append("svg:defs").selectAll("marker")
            .data(["a","b","c"])
          .enter().append("svg:marker")
            .attr("id", String)
            .attr("viewBox", "0 -5 10 10")
            .attr("refX", 15)
            .attr("refY", -1.5)
            .attr("markerWidth", 6)
            .attr("markerHeight", 6)
            .attr("orient", "auto")
          .append("svg:path")
            .attr("d", "M0,-5L10,0L0,5");

        var path = svg.append("svg:g").selectAll("path")
            .data(force.links())
          .enter().append("svg:path")
            .attr("class", function(d) { return "link " + d.type; })
            .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });

        var circle = svg.append("svg:g").selectAll("circle")
            .data(force.nodes())
          .enter().append("svg:circle")
            .attr("r", 6)
            .attr("class", function(d) { return d.type; })
            .call(force.drag);

        var text = svg.append("svg:g").selectAll("g")
            .data(force.nodes())
          .enter().append("svg:g");

        // A copy of the text with a thick white stroke for legibility.
         text.append("svg:text")
            .attr("x", 8)
            .attr("y", ".31em")
            .attr("class", "shadow")
            .text(function(d) { return d.name; });

         text.append("svg:text")
            .attr("x", 8)
            .attr("y", ".31em")
            .attr("class","write")
            .text(function(d) { return d.name; });

        //jQuery update parts for drop downs.
        $(document).ready(function(){
            $('.BU').on('change',function(){
                curBU=$('.BU').val();
                //alert('The selected BU is ' + curBU);

                //Filter links and rebuild nodes based on this.
                minLinks={};
                minLinks=links.filter(function(d){
                    if ((d.type==curBU) || (d.typeKBP==curBU)) {
                        return d
                    }
                })
                //new nodes
                nodes2={};
                nodes2=force.nodes().filter(function(d){return d3.keys(minLinks.filter(function(e){return e.source.name==d.name || e.target.name==d.name;})).length>0});

                // minLinks.forEach(function(d) {
                    // d.source = nodes2[d.source] || (nodes2[d.source] = {name: d.source, type:d.type});
                    // d.target = nodes2[d.target] || (nodes2[d.target] = {name: d.target, type:d.typeKBP});
                // });

                force
                    .nodes(nodes2)
                    .links(minLinks)
                    .start();

                //circle.remove();
                newCirc=circle.data(force.nodes());
                newCirc.enter().append("svg:circle")
                    .attr("r", 6)
                    .attr("class", function(d) { return d.type; })
                    .call(force.drag);
                newCirc.exit().remove();

                newPath=path.data(force.links());
                newPath
                    .enter().append("svg:path")
                    .attr("class", function(d) { return "link " + d.type; })
                    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
                newPath.exit().remove();

                newText=text.data(force.nodes());
                newText.exit().remove();
                newText.select(".shadow").text(function(d){return d.name;});
                newText.select(".write").text(function(d){return d.name;});

            });

        });

        // Use elliptical arc path segments to doubly-encode directionality.
        function tick() {
          path.attr("d", function(d) {
            var dx = d.target.x - d.source.x,
                dy = d.target.y - d.source.y,
                dr = Math.sqrt((dx * dx)/2 + (dy * dy));
            return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
          });

          circle.attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          });

          text.attr("transform", function(d) {
            return "translate(" + d.x + "," + d.y + ")";
          });
        }


    </script>
  </body>
</html>
4

1 回答 1

3

在您的更新代码中,您应该在与新数据进行数据绑定之前重新选择现有节点。circle在当前代码中,您使用的变量path实际上是指选择的新附加节点enter

在每次数据加入之前重新选择是确保加入 DOM 中最新实际状态的最佳方式:

svg.selectAll("path")
    .data(force.links());

svg.selectAll("circle")
    .data(force.nodes());

以某种方式对您的圈子和路径进行分类可能是一个好主意,这样您就可以更直接地为它们选择,这样您就不会意外地在 svg 中找到其他路径或圈子。

此外,请注意对输入选择与更新选择进行操作。考虑到您没有为数据连接定义键,这一点尤其正确,这意味着默认情况下将使用索引,从而使用新数据更新现有节点。例如,在您的代码中,您只是class在新附加的节点上设置属性,您可能希望在所有节点上更新它。

本教程是更好地理解这一点的一个很好的起点: Thinking with Joins

于 2013-08-20T17:38:12.120 回答