2013-03-08 12 views
6

używam d3 narysować wykres strumienia bardzo podobny do oficjalnego przykład http://bl.ocks.org/mbostock/4060954:przejście po dodaniu nowych danych do d3 streamgraph

enter image description here

Jedyną różnicą jest to, jak ja aktualizowany o nowe dane. Nie chcę tylko przejścia w pionie (wartość y), ale chcę też dodać nowe punkty danych po prawej stronie. Cały wykres powinien zostać skompresowany w kierunku poziomym.

Nie było problemu z uzyskaniem pożądanego rezultatu, jedynym problemem jest to, że przejście między tymi dwoma stanami nie wygląda tak, jak powinno.

można znaleźć aa minimalny przykład dziwnym efektem przejścia na JSfiddle: http://jsfiddle.net/jaYJ9/4/

Naciśnij przycisk Aktualizuj, aby zobaczyć efekt

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

Jsfiddle nie działa ... –

+0

Przepraszam, teraz robi – dedan

Odpowiedz

9

Problem ten obraca się wokół faktu, że dla animacji SVG, możesz tylko naciskać punkty na końcu ścieżki.

pierwsze, fix (to będzie działać tylko wtedy, gdy grafika jest zawsze pionowo gęste i mają stałą kolejność dla którego wykres jest najwyższy): przykład

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

Praca tutaj: http://jsfiddle.net/f5JSR/2/

Teraz wyjaśnienie ...

Każdy z kolorowych pasków na wykresie jest zamkniętą ścieżką, a d3.js tworzy je tak, aby żadne z tych pasm kolorów nie zachodziły na siebie. Problem polega na tym, że oznacza to, że każda z tych ścieżek zaczyna się w lewym dolnym rogu, a pętle wracają do siebie. Kiedy dodajesz nowy punkt na tych ścieżkach, dodajesz go na końcu i przesuwa resztę ścieżki w kierunku przeciwnym do ruchu wskazówek zegara (tworząc dziwny efekt animacji).

Początkowo próbowałem rozwiązać ten problem, używając przycinania SVG i właściwości fill-rule: evenodd, ale wydaje się, że aby użyć obcinania, trzeba utworzyć ścieżki złożone, a dodanie nowych punktów powoduje ich przesunięcie na końcu tej złożonej ścieżki (nie można, na przykład, naciśnij punkt na pierwszą ścieżkę ścieżki złożonej), więc problem będzie nadal występował.

Zamiast tego, rozwiązanie to eliminuje sprytność d3.js, a zamiast tego sprawia, że ​​wszystkie pasma kolorów rozszerzają się do dolnej części wykresu (tak robi linia y(null);). Następnie porządkuje ścieżki, więc najwyższe są rysowane jako pierwsze. To podejście rozpada się, jeśli wysokość jednego wykresu spadnie poniżej innej wysokości wykresu.

Wreszcie, kiedy punkty są popychane w prawym dolnym rogu, może dojść do dziwnego artefaktu. Aby to naprawić, podwajam liczbę punktów w prawym dolnym rogu, używając funkcji fixPath.

W każdym razie to rozwiązanie działa na przykładowy przypadek. Mam nadzieję, że to pomoże.

+0

wielkie dzięki, to naprawdę rozwiązało mój problem. Na początku nie zrozumiałem, co masz na myśli przez najwyższą ścieżkę. Ale teraz doszedłem do wniosku, że masz na myśli ten, który jest najwyższy na skumulowanym wykresie. Ponieważ to zamówienie się nie zmieniło, doskonale rozwiązałeś mój problem. Dziękuję również za miłe wyjaśnienie – dedan

Powiązane problemy