2013-03-08 8 views
6

ich d3 einen Strom Diagramm sehr ähnlich dem offiziellen Beispiel ziehen http://bl.ocks.org/mbostock/4060954:Übergang, wenn neue Daten zu d3 streamgraph Zugabe

enter image description here

Der einzige Unterschied ist, wie ich es mit neuen Daten aktualisiert. Ich möchte nicht nur einen vertikalen (y-Wert) Übergang, sondern möchte auch neue Datenpunkte auf der rechten Seite hinzufügen. Der gesamte Graph sollte in horizontaler Richtung komprimiert werden.

Es war kein Problem, das gewünschte Ergebnis zu erzielen, das einzige Problem ist, dass der Übergang zwischen den beiden Zuständen nicht wie erwartet aussieht.

Sie können aa minimal Beispiel für den seltsamen Übergangseffekt auf JSfiddle finden: http://jsfiddle.net/jaYJ9/4/

Drücken Sie die Update-Taste, um den Effekt

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

Die jsfiddle funktioniert nicht ... –

+0

Sorry, jetzt tut es – dedan

Antwort

9

Dieses Problem dreht sich um die Tatsache, dass für SVG-Animationen zu sehen, Sie können nur Punkte auf das Ende eines Pfades drücken.

Zuerst das Update (dies wird nur funktionieren, wenn die Grafik immer vertikal dicht ist und konsistente Ordnung, für die grafische Darstellung ist am höchsten):

... 
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! 
    ... 

Arbeitsbeispiel hier: http://jsfiddle.net/f5JSR/2/

nun die Erläuterung ...

Jedes der Farbbänder in Ihrem Diagramm ist ein geschlossener Pfad, und d3.js konstruiert sie so, dass sich keines dieser Farbbänder überschneidet. Das Problem besteht darin, dass jeder dieser Pfade in der unteren linken Ecke beginnt und den ganzen Weg zurück zu sich selbst führt. Wenn Sie einen neuen Punkt auf diesen Pfaden hinzufügen, fügen Sie ihn am Ende hinzu und schieben den Rest des Pfades gegen den Uhrzeigersinn herum (wodurch dieser seltsame Animationseffekt entsteht).

Ich versuchte zunächst, dies mit SVG Clipping-Lösung und die fill-rule: evenodd Eigenschaft, aber es, dass Clipping zu verwenden scheint, dass Sie zusammengesetzte Pfade zu schaffen haben, und neue Punkte hinzugefügt schiebt sie auf Ende dieses zusammengesetzten Pfad (Sie können nicht, Drücken Sie beispielsweise einen Punkt auf den ersten Pfad des Verbundpfads, damit das Problem weiterhin besteht.

Stattdessen macht diese Lösung mit d3.js der Klugheit und stattdessen macht alle Ihre Farbbänder an der Unterseite des Diagramms erweitern (das ist, was die y(null); Linie tut). Dann ordnet er die Pfade so an, dass die höchsten zuerst gezeichnet werden. Dieser Ansatz bricht zusammen, wenn die Höhe eines Diagramms unter die Höhe eines anderen Diagramms fällt.

Schließlich, wenn Punkte um die untere rechte Ecke geschoben werden, kann es einige seltsame Artefakte geben. Um das zu beheben, verdoppele ich die Anzahl der Punkte in der unteren rechten Ecke mit der fixPath Funktion.

Wie auch immer, diese Lösung funktioniert für den Fall, den Sie hatten. Ich hoffe das hilft.

+0

Vielen Dank, das hat wirklich mein Problem gelöst. Am Anfang habe ich nicht verstanden, was du mit dem _highest path_ meinst. Aber jetzt habe ich herausgefunden, dass du diejenige bedeutest, die in der gestapelten Grafik am höchsten ist. Da sich diese Reihenfolge nicht ändert, hast du mein Problem perfekt gelöst. Danke auch für die nette Erklärung – dedan

Verwandte Themen