2013-03-08 18 views
6

Io uso d3 di tracciare un grafico flusso molto simile all'esempio ufficiale http://bl.ocks.org/mbostock/4060954:transizione quando si aggiungono nuovi dati a D3 streamgraph

enter image description here

L'unica differenza è il modo ho aggiornato con nuovi dati. Non voglio solo una transizione verticale (valore y), ma voglio anche aggiungere nuovi punti dati sulla destra. L'intero grafico dovrebbe essere compresso nella direzione orizzontale.

Non è stato un problema raggiungere il risultato desiderato, l'unico problema è che la transizione tra i due stati non sembra come previsto.

Potete trovare aa esempio minimo dell'effetto di transizione strano JSfiddle: http://jsfiddle.net/jaYJ9/4/

Premere il pulsante di aggiornamento per vedere l'effetto

test_data0 = [{"0": 0.0, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.6, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.6}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.3}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.0}] 
test_data1 = [{"0": 0.0, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.6, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.6}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.3}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.0}] 

$('#update').click(function(){ 
    streamed_history(test_data1) 
}); 
var width = 300, 
    height = 200, 
    colors = {'0': '#6ff500', '1': '#ffad0a', '-1': '#f90035'}, 
    feedbacks = [-1, 0, 1], 
    stack = d3.layout.stack(); 
var svg = d3.select("#timeline").append("svg") 
    .attr("width", width) 
    .attr("height", height); 
var y = d3.scale.linear() 
    .domain([0, 1]) 
    .range([height, 0]); 

streamed_history(test_data0) 

function streamed_history(data) { 
    data_array = feedbacks.map(function (f) { 
     return data.map(function(element, i) { return {x: i, y: element[f]}; }) 
    }), 
    layers = stack(data_array) 
    layers = feedbacks.map(function (f, i) { 
     return {layer: layers[i], feedback: f, color: colors[f]} 
    }) 

    var x = d3.scale.linear() 
     .domain([0, data.length - 1]) 
     .range([0, width]); 

    var area = d3.svg.area().interpolate("basis") 
     .x(function(d) { return x(d.x); }) 
     .y0(function(d) { return y(d.y0); }) 
     .y1(function(d) { return y(d.y0 + d.y); }); 

    //enter 
    svg.selectAll("path") 
     .data(layers) 
     .enter().append("path") 
     .attr("d", function (d) {return area(d.layer);}) 
     .style("fill", function(d) { return d.color; }); 

    //update 
    d3.selectAll("path") 
     .data(layers) 
    .transition() 
     .duration(2000) 
     .attr("d", function (d) {return area(d.layer);}); 
} 
+0

Il jsfiddle non funziona ... –

+0

scusa, ora lo fa – dedan

risposta

9

Questo problema ruota intorno al fatto che, per le animazioni SVG, puoi solo spingere i punti verso la fine di un percorso.

In primo luogo, la correzione (questo funziona solo se la grafica è sempre in verticale dense e hanno ordinamento coerente per il quale il grafico è più alto): Esempio

... 
var area = d3.svg.area().interpolate("basis") 
    ... 
    .y0(function(d) { return y(null); }) // The null here is super important! 
    ... 
... 
// Add this function 
function fixPath (path) { 
    var Lidx = path.indexOf('L'); 
    var Cidx = path.slice(Lidx).indexOf('C'); 
    var PCidx = path.slice(0,Lidx).lastIndexOf('C'); 

    var lp = path.substr(PCidx, Lidx-PCidx); 
    var ss = path.substr(Lidx, Cidx); 

    return (path.slice(0,Lidx) + lp + ss + path.slice(Lidx)); 
} 
... 
svg.selectAll("path") 
    .data(layers.reverse()) // Gotta reverse the order! 
    .attr("d", function (d) { return fixPath(area(d.layer)); }) // Have to double up the bottom right corner to avoid artifacts 
    ... 
... 
d3.selectAll("path") 
    .data(layers) 
    .attr("d", function (d) { return fixPath(area(d.layer)); }) // When updating too! 
    ... 

Lavorare qui: http://jsfiddle.net/f5JSR/2/

Ora, il spiegazione ...

Ciascuna delle bande di colore nel grafico è un tracciato chiuso e d3.js le costruisce in modo che nessuna di queste bande di colore si sovrapponga l'una con l'altra. Il problema è che questo significa che ognuno di questi percorsi inizia nell'angolo in basso a sinistra e scorre indietro fino a se stesso. Quando aggiungi un nuovo punto su questi percorsi, lo aggiungi alla fine e spinge il resto del percorso in senso antiorario (creando quell'effetto di animazione strano).

Inizialmente ho provato a risolverlo usando il clipping SVG e la proprietà fill-rule: evenodd, ma sembra che per usare il clipping devi creare percorsi composti e aggiungere nuovi punti li spinge alla fine di questo percorso composto (non puoi, per esempio, spingere un punto sul primo percorso del percorso composto), quindi il problema persiste.

Invece, questa soluzione elimina l'intelligenza di d3.js e fa invece espandere tutte le bande di colori nella parte inferiore del grafico (è ciò che sta facendo la riga y(null);). Quindi ordina i percorsi in modo che i più alti vengano disegnati per primi. Questo approccio si interrompe se l'altezza di un grafico scende al di sotto dell'altezza di un altro grafico.

Infine, a quando i punti vengono spinti nell'angolo in basso a destra, ci possono essere alcuni artefatti strani. Per risolvere il problema, raddoppio il numero di punti nell'angolo in basso a destra utilizzando la funzione fixPath.

Ad ogni modo, questa soluzione funziona per il caso di esempio che hai avuto. Spero che aiuti.

+0

grazie mille, questo ha davvero risolto il mio problema. All'inizio non ho capito cosa intendi per il sentiero più alto. Ma ora ho capito che intendi quello che è il più alto nel grafico in pila. Poiché questo ordine non cambia, hai risolto perfettamente il mio problema. Grazie anche per la bella spiegazione – dedan

Problemi correlati