简体   繁体   English

消除D3基本折线图过渡中的突然添加/删除

[英]Eliminate sudden additions/deletions in D3 basis line chart transition

I have exactly similar SO question 我有完全类似的问题

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

One difference is that, I have line interpolated as "basis" 一个区别是,我将线插补为“基本”

var line = d3.svg.line().interpolate('basis')

Struggling with this exactly 13 hours :/ please help 正是在这13个小时内挣扎:/请帮助

 <html> <head> <title>Chart</title> <style> path { stroke: #f00; } .line { stroke: #0f0; fill: none; stroke-width: 2px; } .rule { stroke: #ccc; stroke-width: 1px; } </style> </head> <body> <p>I want to get the chart below to transition such that the points on the lines appear to move up and down, not side to side. </p> <p>When transitioning to the smaller data-set especially, I'd like to not have a white gap appear before the lines take shape. </p> <p>Also, the grid-lines should slide into and out of existence, rather than appearing or disappearing. Ideas? </p> <script src="http://d3js.org/d3.v2.min.js"></script> <script> var data = [ [1,8,8,8,8,8,8,8,8,8,8,8,8,8,8], [8,1,1,1,1,1,1,1,1,1,1,1,1,1,1], ]; var data3 = [ [1,1,1], [8,8,8], ]; var w = 500, 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(); // Add path interpolator to d3 d3.interpolators.push(function(a, b) { debugger; var isPath, isArea, interpolator, ac, bc, an, bn, d; // 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; 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); } } isPath = /M-?\\d*\\.?\\d*,-?\\d*\\.?\\d*(L-?\\d*\\.?\\d*,-?\\d*\\.?\\d*)*Z?/; if (isPath.test(a) && isPath.test(b)) { isArea = a[a.length - 1] === 'Z'; ac = extractCoordinates(a); bc = extractCoordinates(b); an = ac.length; bn = bc.length; if (an > bn) { bc = bufferPath(bc, ac); } 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); }; } }); 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().interpolate('basis') .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, data3]; function next() { var it = dats.shift(); dats.push(it); drawdata(it, chart); } setInterval(next, 2000); next(); </script> </body> </html 

Codepen Codepen

Partial answer : for the grid-lines, set an "enter" and "exit" selections using a value greater than width : 部分答案 :对于网格线,使用大于width的值设置“输入”和“退出”选择:

 <html> <head> <title>Chart</title> <style> path { stroke: #f00; } .line { stroke: #0f0; fill: none; stroke-width: 2px; } .rule { stroke: #ccc; stroke-width: 1px; } </style> </head> <body> <script src="https://d3js.org/d3.v3.min.js"></script> <script> 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().interpolate('basis') .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', w+10) .attr('x2', w+10) .attr('y1', 0) .attr('y2', h) .attr('class', 'rule') .transition() .duration(500) .attr('x1', x) .attr('x2', x) .attr('y1', 0) .attr('y2', h); ticks.transition() .ease('linear') .duration(500) .attr('x1', x) .attr('x2', x) .attr('y1', 0) .attr('y2', h); ticks.exit().transition() .duration(500) .attr('x1', w+10) .attr('x2', w+10) .attr('y1', 0) .attr('y2', h) .remove(); } var dats = [data, data2, data3]; function next() { var it = dats.shift(); dats.push(it); drawdata(it, chart); } setInterval(next, 2000); next(); </script> </body> </html> 

I belive that d3 have a more clean solution. 我相信d3有一个更干净的解决方案。 See d3.svg.axis . 参见d3.svg.axis

 var points0 = [ [1,8,8,8,8,8,8,8,8,8,8,8,8,8,8], [8,1,1,1,1,1,1,1,1,1,1,1,1,1,1], ]; var points1 = [ [1,1,1], [8,8,8], ]; var w = 500, h = 100; var chart = d3.select('body').append('div') .attr('class', 'chart') .append('svg') .attr('width', w) .attr('height', h) var color = d3.scale.category10(); function drawdata(data, svg) { 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().interpolate('basis') .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('path.line') .data(data); lines .enter() .append('path') .attr('class', 'line') .style('stroke', (d,i) => color(i)) lines // .attr('d', line) .transition() .ease('linear') .duration(500) .attrTween("d", function(d) { return pathTween(line(d), 4, this)}) lines .exit() .remove(); // legend var ticks = chart.selectAll('line') .data(x.ticks(num)); ticks.enter() .append('line') .attr('x1', w+10) // HACK .attr('x2', w+10) // HACK .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(); } const data = [points0, points1] setInterval(() => { const point = data[0] data.reverse() drawdata(point, chart) }, 1e3); function pathTween(d1, precision, path0) { var path1 = path0.cloneNode(), n0 = path0.getTotalLength(), n1 = (path1.setAttribute("d", d1), path1).getTotalLength(); // Uniform sampling of distance based on specified precision. var distances = [0], i = 0, dt = precision / Math.max(n0, n1); while ((i += dt) < 1) { distances.push(i); } distances.push(1); // Compute point-interpolators at each distance. var points = distances.map(function(t) { var p0 = path0.getPointAtLength(t * n0), p1 = path1.getPointAtLength(t * n1); return d3.interpolate([p0.x, p0.y], [p1.x, p1.y]); }); return function(t) { return "M" + points.map(function(p) { return p(t); }).join("L"); }; } 
 path { stroke: #f00; } .line { stroke: #0f0; fill: none; stroke-width: 2px; } .rule { stroke: #ccc; stroke-width: 1px; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 

Use Peter Beshai's d3-line-chunked ! 使用Peter Beshai的d3线块 It does exactly what you need: "create lines that indicate where data is missing with gaps or differently styled chunks/line segments." 它恰好满足了您的需要: “创建行以指示数据丢失的地方,这些行之间存在间隙或样式不同的块/线段。”

在此处输入图片说明

See also the blog post where he explains various approaches to the same issue: how to display missing data . 另请参阅博客文章,其中他解释了解决同一问题的各种方法: 如何显示丢失的数据

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM