d3.js强制布局拖动在删除节点后停止工作

时间:2014-11-10 03:58:16

标签: javascript d3.js

我看过this answerthis too,但它们不起作用 我的代码在Fiddle

两个问题:
1.单击节点并按键盘上的删除按钮,节点和相应的链接将被删除,但为什么我之后无法拖动剩余的节点?
2.我尝试附加图像(使用nodes数组中的路径),但图像没有出现。圆圈刚刚消失,没有图像出现(图像的路径是正确的。在同一个程序中,我尝试在屏幕的一角显示图像并且工作正常)。

代码:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background { /*stroke: gray; stroke-width: 1px; fill: rgb(252,231,216);*/ cursor: move; }
.link { stroke: #000;  stroke-width: 1.5px; }
.node { fill: #ccc;  /*stroke: #000;*/  stroke-width: 1.5px; cursor: pointer;}
.node.fixed { fill: #f00; cursor: pointer;}
text { font: 10px sans-serif; pointer-events: none; text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff; }
</style>
<body>
<script src="d3/d3.v3.js"></script>
<div id="topologyArea"></div>

<script>
var  nodes = [//it's not necessary to give x and y values to nodes. One gets created for every empty object you insert here like this {}
    {id: 1, x: 470, y: 410, icon: "images/abc.jpg"},
    {id: 2, x: 493, y: 364, icon: "images/abc.jpg"},
    {id: 3, x: 442, y: 365, icon: "images/abc.jpg"},
    {id: 4, x: 467, y: 314, icon: "images/abc.jpg"},
    {id: 5, x: 477, y: 248, icon: "images/fsd.jpg"},
    {id: 6, x: 425, y: 207, icon: "images/sdfs.jpg"},
    {id: 7, x: 402, y: 155, icon: "images/dfs.jpg"},
    {id: 8, x: 369, y: 196, icon: "images/abc.jpg"},
    {id: 9, x: 350, y: 148, icon: "images/abc.jpg"},
    {id: 10, x: 539, y: 222, icon: "images/abc.jpg"},
    {id: 11, x: 594, y: 235, icon: "images/abc.jpg"},
    {id: 12, x: 582, y: 185, icon: "images/abc.jpg"},
    {id: 13, x: 633, y: 200, icon: "images/abc.jpg"}
  ];
var links = [
    {id: 1, source:  0, target:  1},
    {id: 2, source:  1, target:  2},
    {id: 3, source:  0, target:  2},
    {id: 4, source:  1, target:  3},
    {id: 5, source:  3, target:  2},
    {id: 6, source:  3, target:  4},
    {id: 7, source:  4, target:  5},
    {id: 8, source:  5, target:  6},
    {id: 9, source:  5, target:  7},
    {id: 10, source:  6, target:  7},
    {id: 11, source:  6, target:  8},
    {id: 12, source:  7, target:  8},
    {id: 13, source:  9, target:  4},
    {id: 14, source:  9, target: 11},
    {id: 15, source:  9, target: 10},
    {id: 16, source: 10, target: 11},
    {id: 17, source: 11, target: 12},
    {id: 18, source: 12, target: 10}
  ];

var margin = {top: -5, right: -5, bottom: -5, left: -5},  width = 960 - margin.left - margin.right,  height = 500 - margin.top - margin.bottom;
var iconOffset = -10, iconSize = 20;
var mousedown_node = null, mouseup_node = null, mousedown_link = null;
var nodeDeletionActivated = false;

var zoom = d3.behavior.zoom().scaleExtent([0.2, 2]).on("zoom", zoomed);

var svg = d3.select("#topologyArea").append("svg").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).attr('class', 'background').attr("transform", "translate(" + margin.left + "," + margin.right + ")");
var rect = svg.append("rect").attr("fill","transparent").attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom)
            .on("mousedown", mousedownOnBackground);

rect.call(zoom);

var elementHolderLayer = svg.append("g");;
var linkLayer, nodeLayer;

d3.select(window).on("keydown", keydown);// add keyboard callback
redraw(elementHolderLayer);

function redraw(theLayer)//after updating the nodes and links arrays, use this function to re-draw the force graph
{   
    var force = d3.layout.force().size([width, height]).charge(-400).linkDistance(40).on("tick", tick);
    var dragElement = force.drag().on("dragstart", dragstarted);  
    linkLayer = null; nodeLayer = null;

    linkLayer = theLayer.selectAll(".link");
    nodeLayer = theLayer.selectAll(".node");

    linkLayer = linkLayer.data(links, function(d) {return d.id; }).exit().remove();
    linkLayer = theLayer.selectAll(".link").data(links, function(d) {return d.id; }).enter().append("line").attr("class", "link");

    nodeLayer = nodeLayer.data(nodes, function(d) {return d.id; }).exit().remove();
    nodeLayer = theLayer.selectAll(".node").data(nodes, function(d) {return d.id; }).enter().append("circle").attr("class", "node").attr("r", 12)

    .on("dblclick", dblclick).style("fill", function(d,i) { return d3.rgb(i*15, i*15, i*15); })
    .on("mouseup", function(d,i) { mouseup(d,i);})
    .on("mousemove", function(d,i) {mousemove(d,i);})
    .on("mousedown", function(d,i) {mousedown(d,i);})
    .call(dragElement)
    //.classed("dragging", true)
    .classed("fixed", function(d) {d.fixed = true;});

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

}//redraw


function tick() 
{
  linkLayer.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; });

  nodeLayer.attr("cx", function(d) {return d.x; }).attr("cy", function(d) { return d.y; });
}

function dblclick(d) { d3.select(this).classed("fixed", d.fixed = false); }
function dragstarted(d) 
{ 
    console.log("dragstarted for "+this);
    //d3.event.sourceEvent.stopPropagation();
    //d3.select(this).classed("dragging", true); 
    //d3.select(this).classed("fixed", d.fixed = true); 
}
function zoomed() { elementHolderLayer.attr("transform", "translate("+d3.event.translate+")scale(" + d3.event.scale + ")"); }


function spliceLinksForNode(node) //remove the links attached to a node that got deleted
{
  toSplice = links.filter(function(l) { return (l.source === node) || (l.target === node); });
  toSplice.map(function(l) {links.splice(links.indexOf(l), 1); });
}

function keydown() 
{
  //if (!selected_node && !selected_link) return;
  switch (d3.event.keyCode) 
  {
    case 8: 
    {// backspace
    }
    case 46:
    { // delete     
        if (mousedown_node)
        {
            selected_node = mousedown_node;
            if (selected_node) 
            {
                nodes.splice(nodes.indexOf(selected_node), 1);
                spliceLinksForNode(selected_node);
            }
            else if (selected_link) { links.splice(links.indexOf(selected_link), 1); }
            selected_link = null;
            selected_node = null;
            redraw(elementHolderLayer);
        }
      break;
    }
  }
}//keydown

function mousedown(d,i) {  mousedown_node = d;   console.log("mousedown"); }
function mousedownOnBackground() {resetMouseVars();}
function mousemove(d, i) {console.log("mousemove");}
function mouseup(d, i) {console.log("mouseup");}

function resetMouseVars() 
{
  mousedown_node = null;
  mouseup_node = null;
  mousedown_link = null;
}

</script>

1 个答案:

答案 0 :(得分:1)

代码中的redraw函数存在一个问题。

linkLayer = linkLayer.data(links, function(d) {return d.id; })
           .exit()
           .remove(); 

以上行在您的代码中没有用,因为您要使用包含旧数据的链接分配相同的变量。

linkLayer = theLayer.selectAll(".link").data(links, function(d) { return d.id; })
           .enter()
           .append("line")
           .attr("class", "link");

节点也是如此。更改您的代码,如下所示。

//Creating links
linkLayer = theLayer.selectAll(".link").data(links, function(d) {
     return d.id;
});
linkLayer.enter().append("line").attr("class", "link");
linkLayer.exit().remove();

//Creating Nodes with image icons
var gNodes = nodeLayer.enter().append("g")
   .attr("class", "node")
   .on("dblclick", dblclick).style("fill", function(d, i) {
        return d3.rgb(i * 15, i * 15, i * 15);
   })
   .on("mouseup", mouseup)
   .on("mousemove", mousemove)
   .on("mousedown", mousedown)
   .call(dragElement)         
   .classed("fixed", function(d) {
      d.fixed = true;
   });

gNodes.append("circle")
   .attr("r", 12);

gNodes.append("svg:image")
   .attr("class", "circle")
   .attr("xlink:href",function(d){ return d.icon })
   .attr("x", "-8px")
   .attr("y", "-8px")
   .attr("width", "16px")
   .attr("height", "16px");

nodeLayer.exit().remove(); 

为了方便地更新圆圈和图像的位置,我使用g元素对它们进行了分组。因此,您需要使用transform属性更新g元素的位置,而不是更新circle的cx和cy属性。现在,tick函数将如下所示。更新了fiddle

function tick() {
  linkLayer.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; });

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