2016-07-27 14 views
8

Mike Bostock has an example in Bezug auf die Aktualisierung eines Force-Layout. Das Beispiel basiert auf v3 - wie kann dieselbe Funktionalität in v4 repliziert werden?D3 v4: Update Force-Layout

Here's my (pitiful) attempt.

Ich habe the changes zu Auswahlen in der V4 Changelog gelesen, aber der merge Aufruf ist immer noch verwirrend. Insbesondere ist mir nicht klar, wie der Datenbeitritt mit der Simulation nodes() und links() interagiert.

+0

Scheint, auf https://bl.ocks.org/tezzutezzu/cd04b3f1efee4186ff42aae66c87d1a7 getan worden zu sein –

Antwort

0

var width = 300, 
 
    height = 200; 
 

 
var color = d3.scaleOrdinal(d3.schemeCategory20); 
 

 
var nodes = [], 
 
    links = []; 
 

 
var simulation = d3.forceSimulation() 
 
    .force("link", d3.forceLink().id(function(d) { return d.id; })) 
 
    .force("charge", d3.forceManyBody()) 
 
    .force("center", d3.forceCenter(width/2, height/2)); 
 

 
var svg = d3.select("svg"); 
 

 
var linkLayer = svg.append('g').attr('id','link-layer'); 
 
var nodeLayer = svg.append('g').attr('id','node-layer'); 
 

 
// 1. Add three nodes and three links. 
 
setTimeout(function() { 
 
    var a = {id: "a"}, b = {id: "b"}, c = {id: "c"}; 
 
    nodes.push(a, b, c); 
 
    links.push({source: a, target: b}, {source: a, target: c}, {source: b, target: c}); 
 
    start(); 
 
}, 0); 
 

 
// 2. Remove node B and associated links. 
 
setTimeout(function() { 
 
    nodes.splice(1, 1); // remove b 
 
    links.shift(); // remove a-b 
 
    links.pop(); // remove b-c 
 
    start(); 
 
}, 2000); 
 

 
// Add node B back. 
 
setTimeout(function() { 
 
    var a = nodes[0], b = {id: "b"}, c = nodes[1]; 
 
    nodes.push(b); 
 
    links.push({source: a, target: b}, {source: b, target: c}); 
 
    start(); 
 
}, 4000); 
 

 

 
function start() { 
 
    var link = linkLayer.selectAll(".link") 
 
    .data(links, function(d) { return d.source.id + "-" + d.target.id; }); 
 
    
 
    
 
    link.enter().append("line") 
 
    .attr("class", "link"); 
 

 
    link.exit().remove(); 
 

 
    var node = nodeLayer.selectAll(".node") 
 
     .data(nodes, function(d) { return d.id;}); 
 
    
 
    node.enter().append("circle") 
 
     .attr("class", function(d) { return "node " + d.id; }) 
 
     .attr("r", 8); 
 

 
    node.exit().remove(); 
 

 
    simulation 
 
    .nodes(nodes) 
 
    .on("tick", tick); 
 

 
    simulation.force("link") 
 
    .links(links); 
 
} 
 

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

 
    linkLayer.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; }); 
 
}
.link { 
 
    stroke: #000; 
 
    stroke-width: 1.5px; 
 
} 
 

 
.node { 
 
    fill: #000; 
 
    stroke: #fff; 
 
    stroke-width: 1.5px; 
 
} 
 

 
.node.a { fill: #1f77b4; } 
 
.node.b { fill: #ff7f0e; } 
 
.node.c { fill: #2ca02c; }
<script src="https://d3js.org/d3.v4.min.js"></script> 
 
<svg width="300px" height="200px"></svg>

So brauchen Sie nicht wirklich eine d3-Auswahl-merge Ihr Beispiel funktioniert. Der Grund dafür ist, dass Ihre Knotenpositionen und Verknüpfungen durch die Simulation aktualisiert werden. Daher sollten Sie die Knoten und Links beim Start hinzufügen, aber die Aktualisierung der Positionen erfolgt am Ende der Startmethode, wenn die Simulation gestartet wird.

Ein Hauptfehler mit Ihrem ursprünglichen Code war, dass Sie in den Anfangsstadien des Skripts svg.selectAll ('. Node') & svg.selectAll ('. Link') aufgerufen haben. Wenn Sie dies tun, sind keine Knoten oder Links an die SVG gebunden, so dass Sie eine d3-Auswahl mit leeren DOM-Elementen erhalten. Dies ist in Ordnung, wenn Sie Elemente mit enter(). Append() hinzufügen möchten - aber beim Entfernen von Elementen wird dies nicht funktionieren. Mit der gleichen, veralteten d3-Auswahl, um Elemente mit exit() zu entfernen. Remove() funktioniert nicht b/c es gibt keine DOM-Elemente in der d3-Auswahl, die entfernt werden sollen. Sie müssen jedes Mal svg.selectAll() aufrufen, um die DOM-Elemente zu erhalten, die sich gerade im svg befinden.

Ich habe auch ein paar kleinere Änderungen in Ihrem Code vorgenommen. Normalerweise möchten Sie, dass die Links immer unterhalb der Knoten angezeigt werden. Wenn Sie einem SVG Elemente hinzufügen, werden die zuletzt hinzugefügten Knoten an der Spitze platziert. Wenn Sie jedoch zuvor Gruppen hinzufügen (wie ich es mit linkLayer & nodeLayer im Code getan habe), werden alle neu hinzugefügten Links unter allen Elementen in der nodeLayer-Gruppe angezeigt.

Verwandte Themen