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

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

问题描述

我有完全类似的SO问题



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



一个区别是,我有线插值为基础

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


=snippetdata-lang =jsdata-hide =falsedata-console =truedata-babel =false>

 < html> < head> < title>图表< / title> < style>路径{stroke:#f00; } .line {stroke:#0f0; fill:none; stroke-width:2px; } .rule {stroke:#ccc; stroke-width:1px; }< / style> < / head> < body> < p>我想获得下面的图表,以使线条上的点显示为上下移动,而不是左右移动。 < / p> < p>当转换到更小的数据集时,我希望在线形成之前不会出现白色间隙。 < / p> < p>此外,网格线应滑入和滑出存在,而不是出现或消失。想法? < / 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(); //将路径插值器添加到d3 d3.interpolators.push(function(a,b){debugger; var isPath,isArea,interpolator,ac,bc,an,bn,d; //创建一个给定长度的新数组,填充它给定的值函数fill(value,length){return d3.range(length).map(function(){return value;});} //从路径字符串中提取坐标数组extractCoordinates ){return path.substr(1,path.length  - (isArea?2:1))。split('L');} //从坐标数组创建一个路径function makePath + coordinate.join('L')+(isArea?'Z':'');} //用相同位置的坐标缓冲较小的路径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); } //使用缓冲的路径创建一个interpolater(如果两个路径都有相同的长度,//函数将会成为默认字符串interpolator)interpolator = d3.interpolateString(bn> an?makePath(ac):a ,bn?makePath(bc):b); //如果结束值已更改,请确保最终的内插值正确返回bn>一个? interpolator:function(t){return t === 1? b:内插器(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);} ); 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 ',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);下一个(); < / script> < / body>< / html  



Codepen

解决方案

我相信d3有一个更干净的解决方案。请参阅 d3.svg.axis



  var points0 = [[1,8,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],]; 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);} ); var flat = d3.svg.line().x(function(d,i){return x(i);}).y(y(-1)); var lines = chart.selectAll('path.line').data(data); line .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 ()      。去掉(); // 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 ',x).attr('y1',0).attr('y2',h)ticks.exit()。remove();} const data = [points0,points1] setInterval()=& point = data [0] data.reverse()drawdata(point,chart)},1e3); function pathTween(d1,precision,path0){var path1 = path0.cloneNode(),n0 = path0.getTotalLength =(path1.setAttribute(d,d1),path1).getTotalLength(); //根据指定的精度对距离进行均匀采样。 var distance = [0],i = 0,dt = precision / Math.max(n0,n1);而((i + = dt)<1){距离.push(i); }距离.push(1); //在每个距离处计算点插值器。 var points = distance.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]);});返回函数(t){returnM+ points.map(function(p){return p(t);})join(L); }; }  

  path {stroke:#f00;}。 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>


I have exactly similar SO question

Eliminate sudden additions/deletions in D3 line chart transition

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

<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

解决方案

I belive that d3 have a more clean solution. See 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>

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

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