消除D3线图过渡中的突然添加/删除 [英] Eliminate sudden additions/deletions in D3 line chart transition

查看:186
本文介绍了消除D3线图过渡中的突然添加/删除的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您可以在此查看此代码: http://bl.ocks.org/2626142

You can see this code in action here: http://bl.ocks.org/2626142

此代码绘制折线图,​​然后在3个样本数据集之间进行转换。当从小数据集移到大数据集时,额外的数据点突然出现,而不是从现有行中平滑展开。

This code draws a line chart, then transitions between 3 sample data sets. When moving from a small data set to a larger one, the extra data points suddenly appear instead of smoothly unfolding from the existing line.

当从较大的数据集移动到一个较小的一行,在转换之前,该行会突然被截断,以填充整个图表。

When moving from a larger data set to a smaller one, the line is suddenly truncated before transitioning to fill the whole chart.

使用此代码,会突然添加和删除行和网格线。如何消除这些?

With this code there are sudden additions and deletions to the line and gridlines. How do I eliminate those?

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();


推荐答案

我最近遇到了类似的问题,路径的自定义插值器

I faced a similar problem recently, and solved it using a custom interpolator for paths:

// 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

Here's what the original gist looks like with the new interpolator: http://bl.ocks.org/4535474.

它的方法是'缓冲区通过在开始处插入零长度线段来获得较小数据集的路径。效果是,新的细分在线的开始处从单个点扩展,并且未使用的细分类似地向下折叠到单个点。

Its approach is to 'buffer' the smaller dataset's path by inserting zero-length line segments at the beginning. The effect is that new segments expand out of a single point at the start of the line, and unused segments similarly collapse down to a single point.

在不同的数据集之间转换尺寸(显然)不是常见的问题,并且没有通用的解决方案。因为我正在可视化时间序列数据并且在每天/每周/每月间隔之间转换,所以我需要朝向路径末端的段以保持视觉连续性。我可以想象一种情况,你想在路径的开始做同样的事情,或者通过统一地缓冲段来扩展/收缩路径。无论哪种方式,相同的方法都可以工作。

Transitioning between datasets of different sizes (apparently) isn't a common problem, and doesn't have a universal solution. Because I was visualizing time-series data and transitioning between daily/weekly/monthly intervals, I needed the segments towards the end of the path to maintain visual continuity. I can imagine a case in which you'd want to do the same for the beginning of the path, or perhaps expand/contract the path by uniformly buffering segments throughout. Either way the same approach will work.

这篇关于消除D3线图过渡中的突然添加/删除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆