如何控制d3的节点坐标

How to control the coordinates of the nodes of d3

我创建了一个 d3 force 布局,效果很好。现在我将向图表中添加一组数据。我希望我能控制我的新节点的中心。例如,假设中心是(100,100),我希望新节点整体布局成[(50,50)到(150,150)]的矩形区域。

var width = 500,
    height = 500;

var nodes = [{id:0, n:'Tom'}, {id:1, n:'Join'}, {id:2, n:'John'}, {id:3, n:'Bob'}, {id:4, n:'4'}, {id:5, n:'5'}, {id:6, n:'6'}];
var links = [{source:0,target:1},{source:0,target:2},{source:0,target:3},{source:0,target:4},{source:0,target:5},{source:1,target:5},{source:1,target:6}];

// init force 
var force = d3.layout.force()
    .charge(-120)
    .linkDistance(120)
    .size([width, height]);
// init svg 
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height); 
// set tick function
force.on("tick", function () {
    d3.selectAll(".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;
    });

    // controll the coordinates here
    d3.selectAll(".node").attr("transform", function(d){
            if(d.flag == 1){
          d.x = Math.max(50, Math.min(150, d.x));
          d.y = Math.max(50, Math.min(150, d.y));
        }
            return "translate("+d.x+","+d.y+")";
    });



}).on('end', function(){
        svg.selectAll(".node").each(function(d){d.fixed=true;});
});




function setData(ns, ls){
  var update = svg.selectAll(".link").data(ls);
  update.enter().append("line")
      .attr("class", "link")
      .style("stroke-width", 1);
  update.exit().remove();

  update = svg.selectAll(".node").data(ns);
  update.enter().append("g")
      .attr("class", "node")
      .attr("id", function(d){return d.id})
      .call(force.drag)
      .call(function(p){
          p.append("image")
              .attr("class", "nodeimage")
              .attr("width", "30px")
              .attr("height", "30px")
              .attr("x", "-15px")
              .attr("y", "-15px");
          p.append("text")
              .attr("class", "nodetext")
              .attr("dx", "-10px")
              .attr("dy", "20px")
              .style("font-size", "15px")
              .text(function(d){return d.n});
      });
  update.exit().remove();
  update.selectAll(".nodeimage") 
        .each(function() {
         d3.select(this).datum(d3.select(this.parentNode).datum());
        }) 
              .attr("xlink:href", function(d){
                var img;
            if(d.flag == 1){
                img = "http://www.gravatar.com/avatar/1eccef322f0beef11e0e47ed7963189b/?default=&s=80"
            }else{
                img = "http://www.gravatar.com/avatar/a1338368fe0b4f3d301398a79c171987/?default=&s=80";
            }
            return img;
              });
  force.nodes(ns)
      .links(ls)
      .start();
}
//init 
setData(nodes, links);
setTimeout(function(){
        //generate new data and merge to old data
        nodes = nodes.concat(generateNewData());
        setData(nodes, links);
    //how do i control the coordinate of new nodes?
}, 3000);


function generateNewData(){
        var ns = [];
        for(var i = 0; i < 10; i++){
            ns.push({id:i+100,n:'n'+i,flag:1});
    }
    return ns;
}

这是我的 jsfiddle 演示:http://jsfiddle.net/cs4xhs7s/4/

最新的demo显示节点可以显示在矩形中,但是它们的坐标是一样的。希望是可用的部队布局

https://jsfiddle.net/wpnq15mf/1/

var width = 500,
    height = 500;

var nodes = [{id:0, n:'Tom'}, {id:1, n:'Join'}, {id:2, n:'John'}, {id:3, n:'Bob'}, {id:4, n:'4'}, {id:5, n:'5'}, {id:6, n:'6'}];
var links = [{source:0,target:1},{source:0,target:2},{source:0,target:3},{source:0,target:4},{source:0,target:5},{source:1,target:5},{source:1,target:6}];

// init force 
var force = d3.layout.force()
    .charge(-500)
    .linkDistance(120)
    .gravity(0.1)
    .size([width, height]);
// init svg 
var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height); 
// set tick function
force.on("tick", function () {
    d3.selectAll(".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;
    });
  
    // controll the coordinates here
    d3.selectAll(".node").attr("transform", function(d){
          if(d.flag == 1){
                d.x = Math.max(50, Math.min(150, d.x));
                d.y = Math.max(50, Math.min(150, d.y));
              }
      return "translate("+d.x+","+d.y+")";
    });
    
    
    
}).on('end', function(){
  svg.selectAll(".node").each(function(d){d.fixed=true;});
});




function setData(ns, ls){
  var update = svg.selectAll(".link").data(ls);
  update.enter().append("line")
      .attr("class", "link")
      .style("stroke-width", 1);
  update.exit().remove();

  update = svg.selectAll(".node").data(ns);
  update.enter().append("g")
      .attr("class", "node")
      .attr("id", function(d){return d.id})
      .call(force.drag)
      .call(function(p){
          p.append("image")
              .attr("class", "nodeimage")
              .attr("width", "30px")
              .attr("height", "30px")
              .attr("x", "-15px")
              .attr("y", "-15px");
          p.append("text")
              .attr("class", "nodetext")
              .attr("dx", "-10px")
              .attr("dy", "20px")
              .style("font-size", "15px")
              .text(function(d){return d.n});
      });
  update.exit().remove();
  update.selectAll(".nodeimage") 
        .each(function() {
         d3.select(this).datum(d3.select(this.parentNode).datum());
        }) 
              .attr("xlink:href", function(d){
               var img;
            if(d.flagx == 1){
                img = "http://www.gravatar.com/avatar/1eccef322f0beef11e0e47ed7963189b/?default=&s=80"
            }else{
                img = "http://www.gravatar.com/avatar/a1338368fe0b4f3d301398a79c171987/?default=&s=80";
            }
            return img;
              });
  force.nodes(ns)
      .links(ls)
      .start();
}
//init 
setData(nodes, links);
setTimeout(function(){
  //generate new data and merge to old data
  nodes = nodes.concat(generateNewData());
    links = links.concat(generateNewLinks());
  setData(nodes, links);
    //how do i control the coordinate of new nodes?
}, 3000);


function generateNewData(){
  var ns = [];
    ns.push({id:6,n:'n'+i,flag:1, flagx:1});
  for(var i = 1; i < 10; i++){
      ns.push({id:i+6,n:'n'+i, flagx:1});
    }
    return ns;
}

function generateNewLinks(){
  var ns = [];
    ns.push({source:7,target:8});
    ns.push({source:7,target:9});
    ns.push({source:7,target:10});
    ns.push({source:7,target:11});
    ns.push({source:7,target:12});
    ns.push({source:7,target:13});
    ns.push({source:7,target:14});
    ns.push({source:7,target:15});
    ns.push({source:7,target:16});
    return ns;
}
.node {
    stroke: #fff;
    stroke-width: 1.5px;
}
.link {
    stroke: #999;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>