6

私はd3を使用して、公式の例http://bl.ocks.org/mbostock/4060954に非常によく似たストリームグラフを描画します。

ここに画像の説明を入力してください

唯一の違いは、新しいデータで更新する方法です。垂直(y値)遷移だけでなく、右側に新しいデータポイントを追加したい。グラフ全体が水平方向に圧縮されるはずです。

目的の結果を達成することは問題ありませんでした。唯一の問題は、2つの状態間の遷移が期待どおりに表示されないことです。

JSfiddleでの奇妙な遷移効果の最小限の例を見つけることができます: http://jsfiddle.net/jaYJ9/4/

更新ボタンを押して効果を確認してください

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);});
}
4

1 に答える 1

9

この問題は、SVGアニメーションの場合、パスの最後にポイントをプッシュすることしかできないという事実を中心に展開されます。

まず、修正(これは、グラフィックが常に垂直方向に密集していて、グラフが最も高い順序が一貫している場合にのみ機能します):

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

ここでの実例:http://jsfiddle.net/f5JSR/2/

さて、説明...

グラフの各カラーバンドは閉じたパスであり、d3.jsは、これらのカラーバンドが互いに重ならないようにそれらを構築します。問題は、これは、これらの各パスが左下隅から始まり、ループしてそれ自体に戻ることを意味します。これらのパスに新しいポイントを追加すると、最後に追加され、パスの残りの部分が反時計回りに押し出されます(奇妙なアニメーション効果が作成されます)。

最初はSVGクリッピングとfill-rule: evenoddプロパティを使用してこれを解決しようとしましたが、クリッピングを使用するには複合パスを作成する必要があり、新しいポイントを追加すると、この複合パスの最後にそれらがプッシュされます(たとえば、複合パスの最初のパスを指す)、問題は解決しません。

代わりに、このソリューションはd3.jsの巧妙さを排除し、代わりにすべてのカラーバンドをグラフの下部に展開します(これがy(null);線の機能です)。次に、パスを並べ替えて、最も高いパスが最初に描画されるようにします。このアプローチは、あるグラフの高さが別のグラフの高さを下回ると機能しなくなります。

最後に、ポイントが右下隅に押し込まれると、奇妙なアーティファクトが発生する可能性があります。これを修正するには、関数を使用して右下隅のポイント数を2倍にしfixPathます。

とにかく、このソリューションは、あなたが持っていた例のケースで機能します。これがお役に立てば幸いです。

于 2013-03-11T20:14:10.250 に答える