Transition when adding new data to d3 streamgraph

I use d3 to draw a flow chart, very similar to the official example http://bl.ocks.org/mbostock/4060954 :

enter image description here

The only difference is how I updated it with new data. I not only want a vertical transition (y-value), but I also want to add new data points on the right. The entire graph should be compressed in the horizontal direction.

There were no problems to achieve the desired result, the only problem is that the transition between the two states does not look as expected.

You can find a minimal example of the strange transition effect on JSfiddle: http://jsfiddle.net/jaYJ9/4/

Click the refresh button to see the effect.

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(dx); }) .y0(function(d) { return y(d.y0); }) .y1(function(d) { return y(d.y0 + dy); }); //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);}); } 
+6
source share
1 answer

This problem is related to the fact that for SVG animation you can only click the dots at the end of the path.

Firstly, the correction (this will work only if the graph is always vertically dense and has a sequential order for which the graph is the highest):

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

A working example is here: http://jsfiddle.net/f5JSR/2/

Now the explanation ...

Each of the color bars on your chart is a closed path, and d3.js creates them so that none of these color bars overlap with each other. The problem is that this means that each of these paths starts in the lower left corner and cycles back to itself. When you add a new point on these paths, you add it to the end and press the rest of the path counterclockwise (creating this strange animation effect).

At first I tried to solve this using SVG clipping and the fill-rule: evenodd , but it seems that to use clips you need to create complex paths, and adding new points pushes them at the end of this compound path (you cannot, for example, press the point on the first path of the composite path), so the problem persists.

Instead, this solution eliminates the d3.js skill, and instead all the color bars expand to the bottom of the graph (which is what the y(null); line does). Then he arranges the paths, so the highest ones are selected first. This approach is destroyed if the height of one graph falls below another height of the graph.

Finally, when the dots are clicked in the lower right corner, some strange artifact may occur. To fix this, I double the number of points in the lower right corner using the fixPath function.

In any case, this solution works for the example you had. Hope this helps.

+9
source

All Articles