2012-05-07 44 views
3

突然增加/刪除您可以在這裏看到實際的代碼:http://bl.ocks.org/2626142消除在D3線圖過渡

該代碼繪製折線圖,​​然後3樣本數據集之間的過渡。當從一個小數據集移動到一個較大的數據集時,額外的數據點突然出現,而不是從現有的線平滑地展開。

當從一個較大的數據集移動到一個較小的數據集時,在轉換填充整個圖表之前,該行會突然被截斷。

使用此代碼會突然添加和刪除線條和網格線。我如何消除這些?

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

回答

5

我最近遇到了類似的問題,並使用路徑custom interpolator解決它:

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

這裏是原來的要點是什麼樣子與新插值:http://bl.ocks.org/4535474

其方法是通過在開始處插入零長度線段來「緩衝」較小數據集的路徑。其效果是,新線段在線的起始點擴展到單個點,而未使用的線段同樣摺疊到一個點。

不同大小(顯然)的數據集之間的過渡不是一個常見問題,也沒有通用的解決方案。因爲我正在對時間序列數據進行可視化處理,並在日常/每週/每月的時間間隔之間進行轉換,所以我需要朝向路徑末端的分段來保持視覺連續性。我可以想象一種情況,您希望在路徑的開始時執行相同的操作,或者可以通過在整個區域中統一緩衝區段來擴展/收縮路徑。無論哪種方式,相同的方法將起作用。

+0

不錯!謝謝! – iffy

+0

非常感謝你的這種插值,爲我節省了一大筆時間! – tomswift

+0

其他類型的行內插呢?像'基礎' – bumbeishvili