如何使用 CSS 定位节点 Force Directed Graph D3?

How to position nodes Force Directed Graph D3 with CSS?

我正在用 D3 制作一个力定向图,并让它与节点的 svg 圆一起工作。但是,当我将 div 与 CSS left 和 top 属性结合使用时,节点就错位了。我不知道我在这里做错了什么。我使用强制 D3 生成的 x 和 y 坐标作为 left 和 top 属性,但也许这不是正确的方法?

这是我的 JS:

var url = "https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json";

d3.json(url, function(json){

  var data = json;

  var margin = {top: 40, right: 40, bottom: 40, left: 40};

  var w = 1000 - margin.left - margin.right;
  var h = 1000 - margin.top - margin.bottom;

  var svg = d3.select("#chart")
              .append("svg")
                .attr("width", w + margin.left + margin.right)
                .attr("height", h + margin.top + margin.bottom)
              .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var nodes = data.nodes;
  var links = data.links;

  //Create a force layout object and define its properties
  var force = d3.layout.force()
                .size([w,h])
                .nodes(nodes)
                .links(links);

  force.linkDistance(h/20);

  force.charge(-120)

  var link = svg.selectAll(".link")
                .data(links)
                .enter()
                .append("line")
                .attr("class", "link");

  var node = d3.select("#chart").selectAll(".node")
                .data(nodes)
                .enter()
                .append("div")

  force.on("end", function(){
  //set node and link position attributes once force calculations have finished  

    //position the nodes
    node.style("left", function(d){

            return d.x  + "px";

        })
        .style("top", function(d){

            return d.y + "px";

        })
        .attr("class", function(d){

            return "flag flag-" + d.code + " node";

        })
        .attr("src", "https://res.cloudinary.com/dettjqo9j/image/upload/v1485942660/flags_xf9dde.png");

    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;

        })



  })//force.on


  force.start();
  //let the force start its calculations

})

我的css:

svg {

  background-color: white;
  box-shadow: 0 0 10px #888888;

}

.link {

  stroke: #2c3e50;
  stroke-width: 2px;

}

.flag {
    display: inline-block;
    position:absolute;
    width: 16px;
    height: 11px;
    background: url('https://res.cloudinary.com/dettjqo9j/image/upload/v1485942660/flags_xf9dde.png') no-repeat;
}

.flag.flag-ml {
    background-position: -224px -88px;
}

到目前为止在 codepen 上的图表:

https://codepen.io/chemok78/full/VPMgGx/

考虑 SVG 中的链接与 window 的偏移量:它们向上移动了 margin.left,向下移动了 margin.top,并且还向下移动了高度<h1><h2>(加上填充)。

例如,在我的浏览器中,使用以下代码可以正确定位标志:

node.style("left", function(d){

        return d.x + margin.left + 150 + "px";

    })
    .style("top", function(d){

        return d.y + margin.top + "px";

    })

请注意,这是因为 <div> 标志元素的默认 lefttop 属性为 0,即使它们的 parent 是 <div#chart> .我有 hard-coded 150,但您也可以设计一种动态计算方法。

你使用 <circle> 而不是 <div> 的原因是圆是 SVG 元素和 <svg> 的 children,所以它们已经在正确的位置起始位置。可能有另一种解决方案,您可以将标志保留在 SVG 中:将节点添加为 <g><rect> 而不是 <div>xy 的属性代替 lefttop<svg> 的 children 代替 <div#chart>。但是,我还没有探索如何实现将标志分配给每个节点,因为您没有为每个标志文件指向不同的 URL。

谢谢大家,

我最终使用 Element.getBoundingClientRect() 获得了图表的 SVG g 元素的位置,并使用它的左上角位置来定位所有节点。

//Get position of g element of the chart
  var position = document.getElementById("area").getBoundingClientRect();

force.on("tick", function(){

    node.style("left", function(d){

            return d.x  + position.left + "px";

        })
        .style("top", function(d){

            return d.y  + position.top  +  "px";

        })
        .attr("class", function(d){

            return "flag flag-" + d.code + " node";

        })
        .attr("src", "https://res.cloudinary.com/dettjqo9j/image/upload/v1485942660/flags_xf9dde.png")

    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;

        })

  })