2016-08-28 2 views
5

Ich versuche ein einfaches Force-Layout zu implementieren, in dem Knoten (ohne Links) dynamisch hinzugefügt und entfernt werden können. Es ist mir gelungen, das Konzept in D3 Version 3 zu implementieren, aber ich kann es nicht in Version 4 übersetzen. Nach dem Hinzufügen und Aktualisieren von Knoten friert die Simulation ein und eingehende Kreise werden in der oberen linken Ecke des Svg gezeichnet. Weiß jemand warum das der Fall ist? Vielen Dank für jede Hilfe :)Hinzufügen von Knoten dynamisch zu D3 Force Layout in Version 4

Mein Konzept auf dieser Lösung basiert: Adding new nodes to Force-directed layout

JSFiddle: working code in d3 v3

/* Define class */ 
class Planet { 
    constructor(selector) { 
    this.w = $(selector).innerWidth(); 
    this.h = $(selector).innerHeight(); 

    this.svg = d3.select(selector) 
     .append('svg') 
     .attr('width', this.w) 
     .attr('height', this.h); 

    this.force = d3.layout.force() 
     .gravity(0.05) 
     .charge(-100) 
     .size([this.w, this.h]); 

    this.nodes = this.force.nodes(); 
    } 

    /* Methods (are called on object) */ 
    update() { 
    /* Join selection to data array -> results in three new selections enter, update and exit */ 
    const circles = this.svg.selectAll('circle') 
     .data(this.nodes, d => d.id); // arrow function, function(d) { return d.y;} 

    /* Add missing elements by calling append on enter selection */ 
    circles.enter() 
     .append('circle') 
     .attr('r', 10) 
     .style('fill', 'steelblue') 
     .call(this.force.drag); 

    /* Remove surplus elements from exit selection */ 
    circles.exit() 
     .remove(); 

    this.force.on('tick',() => { 
     circles.attr('cx', d => d.x) 
     .attr('cy', d => d.y); 
    }); 

    /* Restart the force layout */ 
    this.force.start(); 
    } 

    addThought(content) { 
    this.nodes.push({ id: content }); 
    this.update(); 
    } 

    findThoughtIndex(content) { 
    return this.nodes.findIndex(node => node.id === content); 
    } 

    removeThought(content) { 
    const index = this.findThoughtIndex(content); 
    if (index !== -1) { 
     this.nodes.splice(index, 1); 
     this.update(); 
    } 
    } 
} 

/* Instantiate class planet with selector and initial data*/ 
const planet = new Planet('.planet'); 
planet.addThought('Hallo'); 
planet.addThought('Ballo'); 
planet.addThought('Yallo'); 

Dies ist meine Absicht, den Code in v4 der Übersetzung:

/* Define class */ 
class Planet { 
    constructor(selector) { 
    this.w = $(selector).innerWidth(); 
    this.h = $(selector).innerHeight(); 

    this.svg = d3.select(selector) 
     .append('svg') 
     .attr('width', this.w) 
     .attr('height', this.h); 

    this.simulation = d3.forceSimulation() 
     .force('charge', d3.forceManyBody()) 
     .force('center', d3.forceCenter(this.w/2, this.h/2)); 

    this.nodes = this.simulation.nodes(); 
    } 

    /* Methods (are called on object) */ 
    update() { 
    /* Join selection to data array -> results in three new selections enter, update and exit */ 
    let circles = this.svg.selectAll('circle') 
     .data(this.nodes, d => d.id); // arrow function, function(d) { return d.y;} 

    /* Add missing elements by calling append on enter selection */ 
    const circlesEnter = circles.enter() 
     .append('circle') 
     .attr('r', 10) 
     .style('fill', 'steelblue'); 

    circles = circlesEnter.merge(circles); 

    /* Remove surplus elements from exit selection */ 
    circles.exit() 
     .remove(); 

    this.simulation.on('tick',() => { 
     circles.attr('cx', d => d.x) 
     .attr('cy', d => d.y); 
    }); 

    /* Assign nodes to simulation */ 
    this.simulation.nodes(this.nodes); 

    /* Restart the force layout */ 
    this.simulation.restart(); 
    } 

    addThought(content) { 
    this.nodes.push({ id: content }); 
    this.update(); 
    } 

    findThoughtIndex(content) { 
    return this.nodes.findIndex(node => node.id === content); 
    } 

    removeThought(content) { 
    const index = this.findThoughtIndex(content); 
    if (index !== -1) { 
     this.nodes.splice(index, 1); 
     this.update(); 
    } 
    } 
} 
+0

Ich bin derzeit mit einem ähnlichen Problem, aber mit der Leinwand fest. Ich habe noch keine Lösung, aber ich denke, Ihr Problem wird in dem Beitrag hervorgehoben, den Sie geteilt haben: ** "Der zweite kritische Teil ist, dass die Knoten- und Link-Arrays auf der force() basieren müssen - andernfalls der Graph und Das Modell wird nicht mehr synchron sein, wenn Knoten gelöscht und hinzugefügt werden. "** Sie fügen die Knoten hinzu, definieren jedoch nicht die Koordinaten. –

Antwort

3

Bitte see plunkr example

Ich bin mit Leinwand, aber die Theorie ist das gleiche:

Sie haben Ihre neue Array von Knoten und Verbindungen zu D3 Kernfunktionen zuerst geben, bevor sie zu dem ursprünglichen Array hinzufügen.

Danach sagen Sie D3, um mit den neuen Daten neu zu starten.

Wenn D3 Ihre tick()-Funktion aufruft, weiß es bereits, welche Koordinaten Sie auf Ihre SVG-Elemente anwenden müssen.

ticked: function(){ 
    if(!this.graph) { 
     return false; 
    } 

    this.context.clearRect(0,0,this.width,this.height); 
    this.context.save(); 
    this.context.translate(this.width/2, this.height/2); 

    this.context.beginPath(); 
    this.graph.links.forEach((d)=>{ 
     this.context.moveTo(d.source.x, d.source.y); 
     this.context.lineTo(d.target.x, d.target.y); 
    }); 
    this.context.strokeStyle = this.lines.stroke.color; 
    this.context.lineWidth = this.lines.stroke.thickness; 

    this.context.stroke(); 

    this.graph.nodes.forEach((d)=>{ 
     this.context.beginPath(); 

     this.context.moveTo(d.x + d.r, d.y); 
     this.context.arc(d.x, d.y, d.r, 0, 2 * Math.PI); 

     this.context.fillStyle = d.colour; 
     this.context.strokeStyle =this.nodes.stroke.color; 
     this.context.lineWidth = this.nodes.stroke.thickness; 
     this.context.fill(); 
     this.context.stroke(); 
    }); 

    this.context.restore(); 
} 

Plunkr example

Verwandte Themen