【问题标题】:d3.js: How to change a nodes' representation in a force-layout graphd3.js:如何在强制布局图中更改节点的表示
【发布时间】:2016-05-17 10:57:56
【问题描述】:

我试图通过在单击时将节点的形状从圆形更改为矩形来扩展 this force layout example。所以我不想更改任何数据,只想替换相应的 SVG 元素。

我的一种方法如下所示:

     node.on("click", function() {
        this.remove();
        svg.selectAll(".node").data(graph.nodes).enter().append("rect")
                .attr("class", "node")
                .attr("width", 5).attr("height", 5)
                .style("fill", function(d) { return color(d.group); });
    });

所以我从 DOM 中删除了 SVG 元素并重新绑定数据,为现在缺失的节点添加了一个矩形。

不幸的是,这不起作用(强制布局没有在新元素上设置任何属性),我不知道这是否是一种合理的方法。

任何想法如何正确地做到这一点?

【问题讨论】:

    标签: javascript d3.js svg force-layout


    【解决方案1】:

    试试这个方法。

    node.on("click", function(d) {
      var size = d.weight * 2 + 12;
      d3.select(this).select("circle").remove();
      d3.select(this).append("rect")
        .attr("x", -(size / 2))
        .attr("y", -(size / 2))
        .attr("height", size)
        .attr("width", size)
        .style("fill", function(d) {
          return color(1 / d.rating);
        });
    });
    

    工作代码sn-p -

    var graph = {
      "nodes": [{
        "name": "1",
        "rating": 90,
        "id": 2951
      }, {
        "name": "2",
        "rating": 80,
        "id": 654654
      }, {
        "name": "3",
        "rating": 80,
        "id": 6546544
      }, {
        "name": "4",
        "rating": 1,
        "id": 68987978
      }, {
        "name": "5",
        "rating": 1,
        "id": 9878933
      }, {
        "name": "6",
        "rating": 1,
        "id": 6161
      }, {
        "name": "7",
        "rating": 1,
        "id": 64654
      }, {
        "name": "8",
        "rating": 20,
        "id": 354654
      }, {
        "name": "9",
        "rating": 50,
        "id": 8494
      }, {
        "name": "10",
        "rating": 1,
        "id": 6846874
      }, {
        "name": "11",
        "rating": 1,
        "id": 5487
      }, {
        "name": "12",
        "rating": 80,
        "id": "parfum_kenzo"
      }, {
        "name": "13",
        "rating": 1,
        "id": 65465465
      }, {
        "name": "14",
        "rating": 90,
        "id": "jungle_de_kenzo"
      }, {
        "name": "15",
        "rating": 20,
        "id": 313514
      }, {
        "name": "16",
        "rating": 40,
        "id": 36543614
      }, {
        "name": "17",
        "rating": 100,
        "id": "Yann_YA645"
      }, {
        "name": "18",
        "rating": 1,
        "id": 97413
      }, {
        "name": "19",
        "rating": 1,
        "id": 97414
      }, {
        "name": "20",
        "rating": 100,
        "id": 976431231
      }, {
        "name": "21",
        "rating": 1,
        "id": 9416
      }, {
        "name": "22",
        "rating": 1,
        "id": 998949
      }, {
        "name": "23",
        "rating": 100,
        "id": 984941
      }, {
        "name": "24",
        "rating": 100,
        "id": "99843"
      }, {
        "name": "25",
        "rating": 1,
        "id": 94915
      }, {
        "name": "26",
        "rating": 1,
        "id": 913134
      }, {
        "name": "27",
        "rating": 1,
        "id": 9134371
      }],
      "links": [{
        "source": 6,
        "target": 5,
        "value": 6,
        "label": "publishedOn"
      }, {
        "source": 8,
        "target": 5,
        "value": 6,
        "label": "publishedOn"
      }, {
        "source": 7,
        "target": 1,
        "value": 4,
        "label": "containsKeyword"
      }, {
        "source": 8,
        "target": 10,
        "value": 3,
        "label": "containsKeyword"
      }, {
        "source": 7,
        "target": 14,
        "value": 4,
        "label": "publishedBy"
      }, {
        "source": 8,
        "target": 15,
        "value": 6,
        "label": "publishedBy"
      }, {
        "source": 9,
        "target": 1,
        "value": 6,
        "label": "depicts"
      }, {
        "source": 10,
        "target": 1,
        "value": 6,
        "label": "depicts"
      }, {
        "source": 16,
        "target": 1,
        "value": 6,
        "label": "manageWebsite"
      }, {
        "source": 16,
        "target": 2,
        "value": 5,
        "label": "manageWebsite"
      }, {
        "source": 16,
        "target": 3,
        "value": 6,
        "label": "manageWebsite"
      }, {
        "source": 16,
        "target": 4,
        "value": 6,
        "label": "manageWebsite"
      }, {
        "source": 19,
        "target": 18,
        "value": 2,
        "label": "postedOn"
      }, {
        "source": 18,
        "target": 1,
        "value": 6,
        "label": "childOf"
      }, {
        "source": 17,
        "target": 19,
        "value": 8,
        "label": "describes"
      }, {
        "source": 18,
        "target": 11,
        "value": 6,
        "label": "containsKeyword"
      }, {
        "source": 17,
        "target": 13,
        "value": 3,
        "label": "containsKeyword"
      }, {
        "source": 20,
        "target": 13,
        "value": 3,
        "label": "containsKeyword"
      }, {
        "source": 20,
        "target": 21,
        "value": 3,
        "label": "postedOn"
      }, {
        "source": 22,
        "target": 20,
        "value": 3,
        "label": "postedOn"
      }, {
        "source": 23,
        "target": 21,
        "value": 3,
        "label": "manageWebsite"
      }, {
        "source": 23,
        "target": 24,
        "value": 3,
        "label": "manageWebsite"
      }, {
        "source": 23,
        "target": 25,
        "value": 3,
        "label": "manageWebsite"
      }, {
        "source": 23,
        "target": 26,
        "value": 3,
        "label": "manageWebsite"
      }]
    }
    
    
    var margin = {
      top: -5,
      right: -5,
      bottom: -5,
      left: -5
    };
    var width = 500 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;
    
    var color = d3.scale.category20();
    
    var force = d3.layout.force()
      .charge(-200)
      .linkDistance(50)
      .size([width + margin.left + margin.right, height + margin.top + margin.bottom]);
    
    var zoom = d3.behavior.zoom()
      .scaleExtent([1, 10])
      .on("zoom", zoomed);
    
    var drag = d3.behavior.drag()
      .origin(function(d) {
        return d;
      })
      .on("dragstart", dragstarted)
      .on("drag", dragged)
      .on("dragend", dragended);
    
    
    var svg = d3.select("#map").append("svg")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.right + ")")
      .call(zoom);
    
    var rect = svg.append("rect")
      .attr("width", width)
      .attr("height", height)
      .style("fill", "none")
      .style("pointer-events", "all");
    
    var container = svg.append("g");
    
    //d3.json('http://blt909.free.fr/wd/map2.json', function(error, graph) {
    
    force
      .nodes(graph.nodes)
      .links(graph.links)
      .start();
    
    
    
    var link = container.append("g")
      .attr("class", "links")
      .selectAll(".link")
      .data(graph.links)
      .enter().append("line")
      .attr("class", "link")
      .style("stroke-width", function(d) {
        return Math.sqrt(d.value);
      });
    
    var node = container.append("g")
      .attr("class", "nodes")
      .selectAll(".node")
      .data(graph.nodes)
      .enter().append("g")
      .attr("class", "node")
      .attr("cx", function(d) {
        return d.x;
      })
      .attr("cy", function(d) {
        return d.y;
      })
      .call(drag);
    
    node.append("circle")
      .attr("r", function(d) {
        return d.weight * 2 + 12;
      })
      .style("fill", function(d) {
        return color(1 / d.rating);
      });
    
    
    force.on("tick", function() {
      link.attr("x1", function(d) {
          return d.source.x;
        })
        .attr("y1", function(d) {
          return d.source.y;
        })
        .attr("x2", function(d) {
          return d.target.x;
        })
        .attr("y2", function(d) {
          return d.target.y;
        });
    
      node.attr("transform", function(d) {
        return "translate(" + d.x + "," + d.y + ")";
      });
    });
    
    var linkedByIndex = {};
    graph.links.forEach(function(d) {
      linkedByIndex[d.source.index + "," + d.target.index] = 1;
    });
    
    function isConnected(a, b) {
      return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index];
    }
    
    node.on("click", function(d) {
      var size = d.weight * 2 + 12;
      d3.select(this).select("circle").remove();
      d3.select(this).append("rect")
        .attr("x", -(size / 2))
        .attr("y", -(size / 2))
        .attr("height", size)
        .attr("width", size)
        .style("fill", function(d) {
          return color(1 / d.rating);
        });
    });
    
    function dottype(d) {
      d.x = +d.x;
      d.y = +d.y;
      return d;
    }
    
    function zoomed() {
      container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    }
    
    function dragstarted(d) {
      d3.event.sourceEvent.stopPropagation();
    
      d3.select(this).classed("dragging", true);
      force.start();
    }
    
    function dragged(d) {
    
      d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
    
    }
    
    function dragended(d) {
    
      d3.select(this).classed("dragging", false);
    }
    .node {
      stroke: #fff;
      stroke-width: 1.5px;
    }
    .node-active {
      stroke: #555;
      stroke-width: 1.5px;
    }
    .link {
      stroke: #555;
      stroke-opacity: .3;
    }
    .link-active {
      stroke-opacity: 1;
    }
    .overlay {
      fill: none;
      pointer-events: all;
    }
    #map {
      border: 2px #555 dashed;
      width: 500px;
      height: 400px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    
    <body>
      <div id="map"></div>
    </body>

    【讨论】:

    • 谢谢。使用 SVG 组元素作为形状的容器就可以了。这样数据就绑定到容器上,我可以很容易地改变包含的形状。
    【解决方案2】:

    您已经更新了 dom 元素,但没有更新强制布局算法使用的数组,因此请在 on click 函数的最后一行执行此操作:

    graph.nodes = svg.selectAll(".node");
    

    (您可能还会发现,如果没有数据键功能,随机节点会变为矩形,而不是您单击的那个)

    【讨论】:

    • 我不认为这是正确的。 graph.nodes 是绑定到图形的实际数据,而 svg.selectAll(".node") 返回选择的 DOM 节点。
    • 但是你是对的,节点是随机变化的(虽然我不太明白这个问题)。不过,我在@Gilsha 的解决方案中没有遇到这种随机性。谁能解释这种行为?
    • 啊,你说得对,我错了,我设计了一个示例,该示例使用了与您的示例不同的图形变量。 PS。 “随机性”与重新绑定数据时需要键函数的事实相关,否则它只会按照找到它们的顺序将数据绑定到选定元素,因此最后一个数据项绑定到新元素,是否是最后一个需要完成的数据项。
    • 我明白了。谢谢你的澄清!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 2015-11-22
    • 2016-08-15
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    相关资源
    最近更新 更多