2016-03-14 11 views
11

Sono nuovo a D3 e sto provando ad aggiornare dinamicamente il grafico se il codice sorgente è stato modificato. Ma non sono in grado di ottenere questo.Aggiorna dinamicamente D3 Sunburst se il codice sorgente è aggiornato (elemento aggiunto o cancellato)

Si prega di verificare this plunkr

Js:

var width = 500, 
    height = 500, 
    radius = Math.min(width, height)/2; 

var x = d3.scale.linear() 
    .range([0, 2 * Math.PI]); 

var y = d3.scale.sqrt() 
    .range([0, radius]); 

var color = d3.scale.category10(); 

var svg = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height) 
    .append("g") 
    .attr("transform", "translate(" + width/2 + "," + (height/2 + 10) + ") rotate(-90 0 0)"); 

var partition = d3.layout.partition() 
    .value(function(d) { 
     return d.size; 
    }); 

var arc = d3.svg.arc() 
    .startAngle(function(d) { 
     return Math.max(0, Math.min(2 * Math.PI, x(d.x))); 
    }) 
    .endAngle(function(d) { 
     return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); 
    }) 
    .innerRadius(function(d) { 
     return Math.max(0, y(d.y)); 
    }) 
    .outerRadius(function(d) { 
     return Math.max(0, y(d.y + d.dy)); 
    }); 

//d3.json("/d/4063550/flare.json", function(error, root) { 
var root = initItems; 

var g = svg.selectAll("g") 
    .data(partition.nodes(root)) 
    .enter().append("g"); 

var path = g.append("path") 
    .attr("d", arc) 
    .style("fill", function(d) { 
     return color((d.children ? d : d.parent).name); 
    }) 
    .on("click", click) 
    .each(function(d) { 
     this.x0 = d.x; 
     this.dx0 = d.dx; 
    }); 


//.append("text") 
var text = g.append("text") 
    .attr("x", function(d) { 
     return y(d.y); 
    }) 
    .attr("dx", "6") // margin 
    .attr("dy", ".35em") // vertical-align 
    .attr("transform", function(d) { 
     return "rotate(" + computeTextRotation(d) + ")"; 
    }) 
    .text(function(d) { 
     return d.name; 
    }) 
    .style("fill", "white"); 

function computeTextRotation(d) { 
    var angle = x(d.x + d.dx/2) - Math.PI/2; 
    return angle/Math.PI * 180; 
} 



function click(d) { 
    console.log(d) 
    // fade out all text elements 
    if (d.size !== undefined) { 
     d.size += 100; 
    }; 
    text.transition().attr("opacity", 0); 

    path.transition() 
     .duration(750) 
     .attrTween("d", arcTween(d)) 
     .each("end", function(e, i) { 
      // check if the animated element's data e lies within the visible angle span given in d 
      if (e.x >= d.x && e.x < (d.x + d.dx)) { 
       // get a selection of the associated text element 
       var arcText = d3.select(this.parentNode).select("text"); 
       // fade in the text element and recalculate positions 
       arcText.transition().duration(750) 
        .attr("opacity", 1) 
        .attr("transform", function() { 
         return "rotate(" + computeTextRotation(e) + ")" 
        }) 
        .attr("x", function(d) { 
         return y(d.y); 
        }); 
      } 
     }); 
} //}); 

// Word wrap! 
var insertLinebreaks = function(t, d, width) { 
    alert(0) 
    var el = d3.select(t); 
    var p = d3.select(t.parentNode); 
    p.append("g") 
     .attr("x", function(d) { 
      return y(d.y); 
     }) 
     // .attr("dx", "6") // margin 
     //.attr("dy", ".35em") // vertical-align 
     .attr("transform", function(d) { 
      return "rotate(" + computeTextRotation(d) + ")"; 
     }) 
     //p 
     .append("foreignObject") 
     .attr('x', -width/2) 
     .attr("width", width) 
     .attr("height", 200) 
     .append("xhtml:p") 
     .attr('style', 'word-wrap: break-word; text-align:center;') 
     .html(d.name); 
    alert(1) 
    el.remove(); 
    alert(2) 
}; 

//g.selectAll("text") 
// .each(function(d,i){ insertLinebreaks(this, d, 50); }); 


d3.select(self.frameElement).style("height", height + "px"); 

// Interpolate the scales! 
function arcTween(d) { 
    var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]), 
     yd = d3.interpolate(y.domain(), [d.y, 1]), 
     yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]); 
    return function(d, i) { 
     return i ? function(t) { 
      return arc(d); 
     } : function(t) { 
      x.domain(xd(t)); 
      y.domain(yd(t)).range(yr(t)); 
      return arc(d); 
     }; 
    }; 
} 

function arcTweenUpdate(a) { 
    console.log(path); 
    var _self = this; 
    var i = d3.interpolate({ x: this.x0, dx: this.dx0 }, a); 
    return function(t) { 
     var b = i(t); 
     console.log(window); 
     _self.x0 = b.x; 
     _self.dx0 = b.dx; 
     return arc(b); 
    }; 
} 

setTimeout(function() { 
    path.data(partition.nodes(newItems)) 
     .transition() 
     .duration(750) 
     .attrTween("d", arcTweenUpdate) 

}, 2000); 

risposta

4

In aggiunta a quanto @Cyril ha suggerito sulla rimozione la seguente riga:

d3.select(self.frameElement).style("height", height + "px"); 

Ho apportato ulteriori modifiche zioni in vostro violino: working fiddle

L'idea è usata qui per aggiungere una funzione updateChart che prende il items e quindi generare il grafico:

var updateChart = function (items) { 
    // code to update the chart with new items 
} 

updateChart(initItems); 

setTimeout(function() { updateChart(newItems); }, 2000); 

Questo non usa la funzione arcTweenUpdate si è creato, ma io cercherà di spiegare il concetto di fondo:

primo luogo, è necessario JOIN i nuovi dati con i dati esistenti:

// DATA JOIN - Join new data with old elements, if any. 
var gs = svg.selectAll("g").data(partition.nodes(root)); 

poi, ENTER per creare nuovi elementi, se necessario:

// ENTER 
var g = gs.enter().append("g").on("click", click); 

Ma, abbiamo anche bisogno di UPDATE i/new path e text nodi esistenti con nuovi dati:

// UPDATE 
var path = g.append("path"); 

gs.select('path') 
    .style("fill", function(d) { 
     return color((d.children ? d : d.parent).name); 
    }) 
    //.on("click", click) 
    .each(function(d) { 
     this.x0 = d.x; 
     this.dx0 = d.dx; 
    }) 
    .transition().duration(500) 
    .attr("d", arc); 


    var text = g.append("text"); 

    gs.select('text') 
    .attr("x", function(d) { 
     return y(d.y); 
    }) 
    .attr("dx", "6") // margin 
    .attr("dy", ".35em") // vertical-align 
    .attr("transform", function(d) { 
     return "rotate(" + computeTextRotation(d) + ")"; 
    }) 
    .text(function(d) { 
     return d.name; 
    }) 
    .style("fill", "white"); 

e, dopo tutto viene creato/aggiornato rimuovere i nodi g che non vengono utilizzati, ad esempio EXIT:

// EXIT - Remove old elements as needed. 
gs.exit().transition().duration(500).style("fill-opacity", 1e-6).remove(); 

intero Questo modello di JOIN + ENTER + UPDATE + EXIT è dimostrata nel seguire articoli di Mike Bostock:

  1. General Update Pattern - I
  2. General Update Pattern - II
  3. General Update Pattern - III
+0

Ottima risposta. Come possiamo fornire il suggerimento (nome e valore) per ogni sezione del grafico? – Kiran

1

per la entrare() e le transizioni per lavorare è necessario dare d3 un modo per identificare ogni elemento nei dati. la funzione .data() ha un secondo parametro che ti permette di restituire qualcosa da usare come id. enter() userà l'id per decidere se l'oggetto è nuovo.

provare a cambiare

path.data(partition.nodes(newItems)) 
.data(partition.nodes(root)); 

a

path.data(partition.nodes(newItems), function(d){return d.name}); 
.data(partition.nodes(root), function(d){return d.name}); 
2

Nel lato il violino la setTimeout non è in esecuzione perché:

d3.select(self.frameElement).style("height", height + "px"); 

otterrete Uncaught SecurityError: Impossibile leggere il Proprietà 'frame' da 'Window': Blocca una cornice con origi n "https://fiddle.jshell.net" dall'accesso a un frame con origine e il setTimeout non viene mai chiamato.

Quindi è possibile rimuovere questa riga d3.select(self.frameElement).style("height", height + "px"); solo per il violino.

A parte questo:

La vostra funzione di timeout dovrebbe essere simile a questo:

setTimeout(function() { 
    //remove the old graph 
    svg.selectAll("*").remove(); 
    root = newItems; 
    g = svg.selectAll("g") 
    .data(partition.nodes(newItems)) 
    .enter().append("g"); 
    /make path 
    path = g.append("path") 
    .attr("d", arc) 
    .style("fill", function(d) { 
     return color((d.children ? d : d.parent).name); 
    }) 
    .on("click", click) 
    .each(function(d) { 
     this.x0 = d.x; 
     this.dx0 = d.dx; 
    }); 
    //make text 
    text = g.append("text") 
    .attr("x", function(d) { 
     return y(d.y); 
    }) 
    .attr("dx", "6") // margin 
    .attr("dy", ".35em") // vertical-align 
    .attr("transform", function(d) { 
     return "rotate(" + computeTextRotation(d) + ")"; 
    }) 
    .text(function(d) { 
     return d.name; 
    }) 
    .style("fill", "white"); 

} 

lavorare violino here

+0

ho bisogno di soluzione in cui non sto rimuovere completamente l'intero grafico e ridisegnando di nuovo l'intero grafico dovrebbe invece essere qualcosa di simile a ciò che accade al clic dove aggiorniamo il grafico con animazione Twean. –