Force-Directed Graph,节点不移动并卡在地图的中心

Force-Directed Graph, nodes are not moving and stuck in the center of the map

我有一个力图,其中我的节点实际上是一个带有附加圆圈和附加文本元素的“g”元素。

我的节点有问题,因为它们在拖动时不会移动并且没有定位在链接的尖端。

如果我只将我的节点更改为简单的圆圈,那么它工作正常。但是我无法让它与“g”元素一起工作。

有人建议我使用 ".attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")";});"在我的 ticked() 函数中,因为“g”元素没有 cx 和 cy 值(因为它们是特定于圆圈的)。

我认为问题出在我创建 drag() 函数的方式上。

非常欢迎任何帮助



  ForceGraph(){
    var links = [
        {src:"John",target:"Aurora"},
        {src:"John",target:"Mary"},
        {src:"John",target:"Erik"},
        {src:"John",target:"Susan"},
        {src:"John",target:"Mel"},
   ]

    var nodes = [
        {id:"John"},
        {id:"Aurora"},
        {id:"Mary"},
        {id:"Erik"},
        {id:"Susan"},
        {id:"Mel"},
    ]

    var width = this.$refs.mapFrame.clientWidth // scale to parent container
    var height = this.$refs.mapFrame.clientHeight // scale to parent container


    // Compute values.
    var nodeId = d => d.id // given d in nodes, returns a unique identifier (string)
    const N = d3.map(nodes, nodeId);
   const nodeTitle = (_, i) => N[i];
    const T = d3.map(nodes, nodeTitle);
    
    // Replace the input nodes and links with mutable objects for the simulation.
    nodes = nodes.map(n => Object.assign({}, n));
    links = links.map(l => ({
        orig: l,
        source: l.src,
        target: l.target
    }));
  

    // Construct the forces.
    const forceNode = d3.forceManyBody();
    const forceLink = d3.forceLink(links).id(({index: i}) => N[i]);
    forceNode.strength(-450);
    forceLink.strength(1);
    forceLink.distance(100)


    const simulation = d3.forceSimulation(nodes)
        .force(link, forceLink)
        .force("charge", forceNode)
        .force("x", d3.forceX())
        .force("y", d3.forceY())
        .on("tick", ticked);




    const svg = d3.create("svg")
    .attr("id", "svgId")
        .attr("preserveAspectRatio", "xMidYMid meet")
        .attr("viewBox", [-width/2,-height/2, width,height])
        .classed("svg-content-responsive", true)



    const link = svg.append("g")
        .selectAll("line")
        .data(links)
        .join("line").attr("stroke", "white")
        .attr("stroke-width", "5")
        ;



     var node = svg
        .selectAll(".circle-group")
        .data(nodes)
        .join(enter => {
          node = enter.append("g")        
            .attr("class", "circle-group")
            .call(drag(simulation));
          node.append("circle")
            .attr("class", "background") 
            .style("fill", "blue")
            .attr("r", 30)
           // .call(drag(simulation));
          node.append("text")
            .attr("class", "foreground")
            .attr("dx", function(){return -20})
            .style('font-size', 30 * 0.4 + 'px')
            .text(({index: i}) => T[i])
           // .call(drag(simulation))
            
          node.attr("stroke", "grey"); 
        })
        node.call(drag(simulation));
        



    function ticked() {
        link
        .attr("x1", d => d.source.x)
        .attr("y1", d => d.source.y)
        .attr("x2", d => d.target.x)
        .attr("y2", d => d.target.y);
        node
        .attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")";});
       // .attr("cx", d => d.x)
       // .attr("cy", d => d.y);
    }


    function drag(simulation) {    
        function dragstarted(event) {
        if (!event.active) simulation.alphaTarget(0.3).restart();
        event.subject.fx = event.subject.x;
        event.subject.fy = event.subject.y;
        }
        
        function dragged(event) {
        event.subject.fx = event.x;
        event.subject.fy = event.y;
        }
        
        function dragended(event) {
        if (!event.active) simulation.alphaTarget(0);
        event.subject.fx = null;
        event.subject.fy = null;
        }

        return d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended);
    }



    return  Object.assign(svg.node() );
    }

您的 node 变量应该为空,这意味着您没有更新 tick 函数中的任何内容。这是您加入的一个简短和孤立的示例,之后我调用 selection.size() 以查看有多少项目 selected with node:

var nodes = [1,2,3];
var svg = d3.select("body").append("svg");
     
var node = svg
   .selectAll(".circle-group")
   .data(nodes)
   .join(enter => {
       node = enter.append("g")        
         .attr("class", "circle-group")
       node.append("circle")
            .attr("class", "background") 
            .style("fill", "blue")
            .attr("r", 30)
       node.append("text")
           
          node.attr("stroke", "grey"); 
})

console.log(node.size());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>

这是预期的,加入 returns 合并进入并更新 selections;然而,由于我们提供了一个自定义输入函数,我们需要显式 return 输入的节点(如果提供一个自定义更新函数,我们也需要这样做)。来自文档:

The selections returned by the enter and update functions are merged and then returned by selection.join

如果我们将 return node 添加到您的输入函数的末尾,我们可以看到我们的 select 离子大小现在是 3。

var nodes = [1,2,3];
var svg = d3.select("body").append("svg");
     
var node = svg
   .selectAll(".circle-group")
   .data(nodes)
   .join(enter => {
       node = enter.append("g")        
         .attr("class", "circle-group")
       node.append("circle")
            .attr("class", "background") 
            .style("fill", "blue")
            .attr("r", 30)
       node.append("text")
           
          node.attr("stroke", "grey"); 
      return node;
})

console.log(node.size());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>

现在 node 包括输入的节点,刻度函数应该按预期工作。

另一种方法是 select g 元素每个刻度用 d3.select(".circle-group") 而不是使用 node,但是,这不是很高效。