Eliminate sudden additions / deletions in transition to D3 line chart

This code can be seen here: http://bl.ocks.org/2626142

This code draws a line chart, then transitions between 3 sets of sample data. When moving from a small data set to a larger one, instead of smoothly expanding from an existing row, additional data points unexpectedly appear.

When moving from a larger dataset to a smaller one, the line will be automatically truncated before the transition to fill the entire chart.

With this code, sudden additions and deletions of grid lines and lines appear. How can I fix them?

var data = [
    [0,2,3,2,8],
    [2,4,1,5,3],
];
var data2 = [
    [0,1,2,3,4,5],
    [9,8,7,6,5,6],
];
var data3 = [
    [1,3,2],
    [0,8,5],
];

var w = 300,
    h = 100;

var chart = d3.select('body').append('div')
    .attr('class', 'chart')
    .append('svg:svg')
    .attr('width', w)
    .attr('height', h);

var color = d3.scale.category10();

function drawdata(data, chart) {
    var num = data[0].length-1;
    var x = d3.scale.linear().domain([0, num]).range([0,w]);
    var y = d3.scale.linear().domain([0, 10]).range([h, 0]);

    var line = d3.svg.line()
        .x(function(d, i) { return x(i); })
        .y(function(d) { return y(d); });

    var flat = d3.svg.line()
        .x(function(d, i) { return x(i); })
        .y(y(-1));

    var lines = chart.selectAll('.line')
        .data(data);

    lines.enter().append('path')
            .attr('class', 'line')
            .style('stroke', function(d,i) { return color(i); })
            .attr('d', line);

    lines.transition()
        .ease('linear')
        .duration(500)
        .attr('d', line);

    lines.exit().remove();

    // legend
    var ticks = chart.selectAll('line')
        .data(x.ticks(num));

    ticks.enter().append('line')
            .attr('x1', x)
            .attr('x2', x)
            .attr('y1', 0)
            .attr('y2', h)
            .attr('class', 'rule');
    ticks.transition()
        .ease('linear')
        .duration(500)
        .attr('x1', x)
        .attr('x2', x)
        .attr('y1', 0)
        .attr('y2', h);
    ticks.exit().remove();
}
var dats = [data, data2, data3];
function next() {
    var it = dats.shift();
    dats.push(it);
    drawdata(it, chart);
}
setInterval(next, 2000);
next();
+3
source share
1 answer

:

// Add path interpolator to d3
d3.interpolators.push(function(a, b) {
  var isPath, isArea, interpolator, ac, bc, an, bn;

  // Create a new array of a given length and fill it with the given value
  function fill(value, length) {
    return d3.range(length)
      .map(function() {
        return value;
      });
  }

  // Extract an array of coordinates from the path string
  function extractCoordinates(path) {
    return path.substr(1, path.length - (isArea ? 2 : 1)).split('L');
  }

  // Create a path from an array of coordinates
  function makePath(coordinates) {
    return 'M' + coordinates.join('L') + (isArea ? 'Z' : '');
  }

  // Buffer the smaller path with coordinates at the same position
  function bufferPath(p1, p2) {
    var d = p2.length - p1.length;

    // Paths created by d3.svg.area() wrap around such that the 'end'
    // of the path is in the middle of the list of coordinates
    if (isArea) {
      return fill(p1[0], d/2).concat(p1, fill(p1[p1.length - 1], d/2));
    } else {
      return fill(p1[0], d).concat(p1);
    }
  }

  // Regex for matching the 'd' attribute of SVG paths
  isPath = /M-?\d*\.?\d*,-?\d*\.?\d*(L-?\d*\.?\d*,-?\d*\.?\d*)*Z?/;

  if (isPath.test(a) && isPath.test(b)) {
    // A path is considered an area if it closes itself, indicated by a trailing 'Z'
    isArea = a[a.length - 1] === 'Z';
    ac = extractCoordinates(a);
    bc = extractCoordinates(b);
    an = ac.length;
    bn = bc.length;

    // Buffer the ending path if it is smaller than the first
    if (an > bn) {
      bc = bufferPath(bc, ac);
    }

    // Or, buffer the starting path if the reverse is true
    if (bn > an) {
      ac = bufferPath(ac, bc);
    }

    // Create an interpolater with the buffered paths (if both paths are of the same length,
    // the function will end up being the default string interpolator)
    interpolator = d3.interpolateString(bn > an ? makePath(ac) : a, an > bn ? makePath(bc) : b);

    // If the ending value changed, make sure the final interpolated value is correct
    return bn > an ? interpolator : function(t) {
      return t === 1 ? b : interpolator(t);
    };
  }
});

gist : http://bl.ocks.org/4535474.

"" , . , , .

(-) . // , , . , , , , / . .

+5

All Articles