2017-01-17 17 views
1

以下是「正在工作」的D3動畫。防止D3在更新中添加重複項

在鼠標單擊時,子節點成功消失...但是,添加了重複節點(「圓圈」)。如果你運行下面的代碼,然後摺疊和打開節點,你會看到節點出現在其他節點之上!

圓形元素的重複也可以在鉻檢查中看到。

只是簡單的崩潰和開放是我們在這裏之後。非常感謝您的幫助 !謝謝。

(忽略節點標籤 - 他們並不重要)

<html> 
 
<head> 
 
<style> 
 
    .node { 
 
     cursor: pointer; 
 
     font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; 
 
     font-weight: 300; 
 
    } 
 

 
    .node .text { 
 
     fill: white; 
 
    } 
 

 
    .ORG .circle { 
 
     fill: #1d3649; 
 
    } 
 

 
    .EMR .circle { 
 
     fill: #B2D0F5; 
 
     stroke: #5596e6; 
 
     stroke-dasharray: 3px, 3px; 
 
     opacity: .5; 
 
    } 
 

 
    .EMR .circle:hover { 
 
     fill: #5596e6; 
 
    } 
 

 
    .link { 
 
     fill: none; 
 
     stroke: #eee; 
 
     stroke-width: 1.5px; 
 
     font: 10px sans-serif; 
 
    } 
 

 
    .link.active { 
 
     stroke: #ddd; 
 
     stroke-width: 2; 
 
    } 
 

 
    .arrow { 
 
     fill: #666; 
 
    } 
 

 
    .arrow.active { 
 
     stroke-width: 0 !important; 
 
    } 
 

 
</style> 
 
</head> 
 
<body> 
 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> 
 
<script src="//d3js.org/d3.v3.min.js"></script> 
 
<script> 
 
    var dataset = { 
 
     "nodes": [{ 
 
      "id": 223, 
 
      "type": "Parent", 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 136525, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6090", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 146525, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6090", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 156525, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6090", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 166525, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6090", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 176525, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6090", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136448, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6094", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136328, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6082", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136305, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6096", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136303, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6093", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136299, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6091", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136261, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6089", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136212, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6087", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136115, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6078", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 136113, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6088", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 135843, 
 
      "type": "Child", 
 
      "properties": { 
 
       "patient": "6072", 
 
       "batch": "70" 
 
      } 
 
     }, { 
 
      "id": 555, 
 
      "type": "Grandchild", 
 
      "properties": { 
 

 
      } 
 
     }], 
 
     "edges": [{ 
 
      "id": 0, 
 
      "from": 136113, 
 
      "to": 555, 
 
      "properties": { 
 

 
      } 
 

 
     },{ 
 
      "id": 0, 
 
      "from": 136525, 
 
      "to": 555, 
 
      "properties": { 
 

 
      } 
 
     },{ 
 
      "id": 0, 
 
      "from": 146525, 
 
      "to": 555, 
 
      "properties": { 
 

 
      } 
 
     },{ 
 
      "id": 0, 
 
      "from": 156525, 
 
      "to": 555, 
 
      "properties": { 
 

 
      } 
 
     },{ 
 
      "id": 0, 
 
      "from": 166525, 
 
      "to": 136448, 
 
      "properties": { 
 

 
      } 
 
     },{ 
 
      "id": 0, 
 
      "from": 176525, 
 
      "to": 223, 
 
      "properties": { 
 

 
      } 
 
     },{ 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136525, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136448, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136328, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136305, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 136525, 
 
      "to": 136303, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136299, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136261, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136212, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136115, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 136113, 
 
      "properties": { 
 

 
      } 
 
     }, { 
 
      "id": 0, 
 
      "from": 223, 
 
      "to": 135843, 
 
      "properties": { 
 

 
      } 
 
     }] 
 
    } 
 

 
    var width = 0.975 * $(window).width(), 
 
     height = 0.95 * $(window).height(); 
 

 
    var svg = d3.select("body").append("svg") 
 
     .attr("width", width) 
 
     .attr("height", height); 
 

 
    var force = d3.layout.force() 
 
     .size([width, height]) 
 
     //gravity(0.2) 
 
     .linkDistance(height/6) 
 
     .charge(function(node) { 
 
      if (node.type !== 'ORG') return -2000; 
 
      return -30; 
 
     }); 
 

 
    // build the arrow. 
 
    svg.append("svg:defs").selectAll("marker") 
 
     .data(["end"]) // Different link/path types can be defined here 
 
     .enter().append("svg:marker") // This section adds in the arrows 
 
     .attr("id", function(d) { 
 
      return d; 
 
     }) 
 
     .attr("viewBox", "0 -5 10 10") 
 
     .attr("refX", 12) 
 
     .attr("refY", 0) 
 
     .attr("markerWidth", 9) 
 
     .attr("markerHeight", 5) 
 
     .attr("orient", "auto") 
 
     .attr("class", "arrow") 
 
     .append("svg:path") 
 
     .attr("d", "M0,-5L10,0L0,5"); 
 

 
    var json = dataset; 
 

 
    var edges = []; 
 
    json.edges.forEach(function(e) { 
 
     var sourceNode = json.nodes.filter(function(n) { 
 
       return n.id === e.from; 
 
      })[0], 
 
      targetNode = json.nodes.filter(function(n) { 
 
       return n.id === e.to; 
 
      })[0]; 
 

 
     edges.push({ 
 
      source: sourceNode, 
 
      target: targetNode, 
 
      value: e.id 
 
     }); 
 
    }); 
 

 
    var colors = {}; 
 
    colors[23] = "lightblue"; 
 
    colors[25] = "lightgreen"; 
 
    colors[48] = "lightyellow"; 
 
    colors[28] = "lightblue"; 
 
    colors[5] = "lightgreen"; 
 
    colors[3] = "lightyellow"; 
 
    colors[99] = "lightblue"; 
 
    colors[61] = "lightgreen"; 
 
    colors[12] = "lightyellow"; 
 

 
    for(var i=0; i<json.nodes.length; i++) { 
 
     json.nodes[i].collapsing = 0; 
 
     json.nodes[i].collapsed = false; 
 
     json.nodes[i].radius = json.nodes[i].id % 100; 
 
     if(colors[json.nodes[i].radius] != undefined) 
 
      json.nodes[i].color = colors[json.nodes[i].radius]; 
 
     else 
 
      json.nodes[i].color = "lightbrown"; 
 
    } 
 

 
    var link = svg.selectAll(".link"); 
 
    var node = svg.selectAll(".node"); 
 

 
    force.on("tick", function() { 
 
     // make sure the nodes do not overlap the arrows 
 
     link.attr("d", function(d) { 
 
      // Total difference in x and y from source to target 
 
      diffX = d.target.x - d.source.x; 
 
      diffY = d.target.y - d.source.y; 
 

 
      // Length of path from center of source node to center of target node 
 
      pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY)); 
 

 
      // x and y distances from center to outside edge of target node 
 
      offsetX = (diffX * d.target.radius)/pathLength; 
 
      offsetY = (diffY * d.target.radius)/pathLength; 
 

 
      return "M" + d.source.x + "," + d.source.y + "L" + (d.target.x - offsetX) + "," + (d.target.y - offsetY); 
 
     }); 
 

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

 
    update(); 
 

 
    function update(){ 
 
     var nodes = json.nodes.filter(function(d) { 
 
      return d.collapsing == 0; 
 
     }); 
 

 
     var links = edges.filter(function(d) { 
 
      return d.source.collapsing == 0 && d.target.collapsing == 0; 
 
     }); 
 

 
     link = svg.selectAll(".link").data(links); 
 
     link.exit().remove(); 
 

 
     link.enter().append("path") 
 
      .attr("class", "link") 
 
      .attr("marker-end", "url(#end)"); 
 

 
     node = svg.selectAll(".node").data(nodes); 
 
     node.exit().remove(); 
 

 
     node.enter().append("g") 
 
      .attr("class", function(d) { 
 
       return "node " + d.type 
 
      }); 
 

 
     node.append("circle") 
 
      .attr("class", "circle") 
 
      .attr("r", function(d) { 
 
       //d.radius = 30; 
 
       return d.radius 
 
      }) 
 
      .attr("fill", function(d) { 
 
       //d.radius = 30; 
 
       return d.color; 
 
      }) 
 
      .attr("stroke", function(d) { 
 
       //d.radius = 30; 
 
       return "darkgray"; 
 
      }); 
 
     // return a radius for path to use 
 

 

 

 
     node.append("text") 
 
      .attr("x", 0) 
 
      .attr("dy", ".35em") 
 
      .attr("text-anchor", "middle") 
 
      .attr("class", "text") 
 
      .text(function(d) { 
 
       return d.type 
 
      }); 
 

 
     // On node hover, examine the links to see if their 
 
     // source or target properties match the hovered node. 
 
     node.on('mouseover', function(d) { 
 
      link.attr('class', function(l) { 
 
       if (d === l.source || d === l.target) 
 
        return "link active"; 
 
       else 
 
        return "link inactive"; 
 
      }); 
 
     }); 
 

 
     // Set the stroke width back to normal when mouse leaves the node. 
 
     node.on('mouseout', function() { 
 
      link.attr('class', "link"); 
 
     }) 
 
      .on('click', click); 
 

 
     /** this is NOT the problem **/ 
 
     function click(d) { 
 
      if (!d3.event.defaultPrevented) { 
 
       var inc = d.collapsed ? -1 : 1; 
 
       recurse(d); 
 

 
       function recurse(sourceNode){ 
 
        //check if link is from this node, and if so, collapse 
 
        edges.forEach(function(l) { 
 
         if (l.source.id === sourceNode.id){ 
 

 
          l.target.collapsing += inc; 
 
          recurse(l.target); 
 

 

 
         } 
 
        }); 
 
       } 
 
       d.collapsed = !d.collapsed; 
 
      } 
 
      update(); 
 
     } 
 
     force 
 
      .nodes(nodes) 
 
      .links(links) 
 
      .start(); 
 
    } 
 

 
</script> 
 

 
</body> 
 
</html>

[根據此代碼:http://jsfiddle.net/sheilak/9wvmL8q8/

回答

2

的問題是在這裏:

node.enter().append("g")... 

node.append("circle")... 

node.append("text")... 

雖然你所添加組到enter選擇,您要添加的圓圈和文本到node選擇這是過渡元素

嘗試:

var nodeEnter = node.enter().append("g")... 

nodeEnter.append("circle")... 

nodeEnter.append("text")... 

在這種情況下,nodeEnter將等於到鏈中最後創建的元素,在本例中爲g元素。