d3带路径和SVG的动画折线图

时间:2017-11-21 00:38:52

标签: javascript d3.js graph

我试图将动画线示例here调整为折线图



function displayGraph(id, data, width, height, interpolation, animate, updateDelay, transitionDelay) {
  // create an SVG element inside the #graph div that fills 100% of the div
  var graph = d3.select(id).append("svg:svg").attr("width", "100%").attr("height", "100%");

  // create a simple data array that we'll plot with a line (this array represents only the Y values, X will just be the index location)


  // X scale will fit values from 0-10 within pixels 0-100
  var x = d3.scale.linear().domain([0, 48]).range([-5, width]); // starting point is -5 so the first value doesn't show and slides off the edge as part of the transition
  // Y scale will fit values from 0-10 within pixels 0-100
  var y = d3.scale.linear().domain([0, 10]).range([0, height]);

  // create a line object that represents the SVN line we're creating
  var line = d3.svg.line()
    // assign the X function to plot our line as we wish
    .x(function(d, i) {
      // verbose logging to show what's actually being done
      //console.log('Plotting X value for data point: ' + d + ' using index: ' + i + ' to be at: ' + x(i) + ' using our xScale.');
      // return the X coordinate where we want to plot this datapoint
      return x(i);
    })
    .y(function(d) {
      // verbose logging to show what's actually being done
      //console.log('Plotting Y value for data point: ' + d + ' to be at: ' + y(d) + " using our yScale.");
      // return the Y coordinate where we want to plot this datapoint
      return y(d);
    })
    .interpolate(interpolation)

  // display the line by appending an svg:path element with the data line we created above
  graph.append("svg:path").attr("d", line(data));
  // or it can be done like this
  //graph.selectAll("path").data([data]).enter().append("svg:path").attr("d", line);


  function redrawWithAnimation() {
    // update with animation
    graph.selectAll("path")
      .data([data]) // set the new data
      .attr("transform", "translate(" + x(1) + ")") // set the transform to the right by x(1) pixels (6 for the scale we've set) to hide the new value
      .attr("d", line) // apply the new data values ... but the new value is hidden at this point off the right of the canvas
      .transition() // start a transition to bring the new value into view
      .ease("linear")
      .duration(transitionDelay) // for this demo we want a continual slide so set this to the same as the setInterval amount below
      .attr("transform", "translate(" + x(0) + ")"); // animate a slide to the left back to x(0) pixels to reveal the new value

    /* thanks to 'barrym' for examples of transform: https://gist.github.com/1137131 */
  }

  function redrawWithoutAnimation() {
    // static update without animation
    graph.selectAll("path")
      .data([data]) // set the new data
      .attr("d", line); // apply the new data values
  }
  setInterval(function() {
    if (animate) {
      redrawWithAnimation();
    } else {
      redrawWithoutAnimation();
    }
  }, updateDelay);
} //displayGraph

// input data
var data = [3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 3, 6, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 9, 3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 9];

// display
displayGraph("#graph1", data, 300, 30, "basis", true, 1000, 1000);

// update data
setInterval(function() {
  var v = data.shift(); // remove the first element of the array
  data.push(v); // add a new element to the array (we're just taking the number we just shifted off the front and appending to the end)
}, 1000);

path {
  stroke: steelblue;
  stroke-width: 1;
  fill: none;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="graph1" class="aGraph" style="width:300px; height:30px;"></div>
&#13;
&#13;
&#13;

虽然在示例中我们绘制了一条简单的行,因此数据由x值数组表示,索引为y,我的数据集就像

var data=[ 
{"progress":42.3,"words":2116,"lr":0.288598,"loss":4.07032,"eta":"0h0m"}, {"progress":44,"words":2197,"lr":0.279892,"loss":4.06091,"eta":"0h0m"},{"progress":45.7,"words":2279,"lr":0.27161,"loss":4.053332,"eta":"0h0m"},{"progress":46.6,"words":2364,"lr":0.267103,"loss":4.052618,"eta":"0h0m"},{"progress":49.1,"words":2449,"lr":0.254353,"loss":4.055149,"eta":"0h0m"}, {"progress":50.9,"words":2532,"lr":0.245493,"loss":4.057263,"eta":"0h0m"},{"progress":52.7,"words":2617,"lr":0.236479,"loss":4.059458,"eta":"0h0m"},{"progress":57,"words":2833,"lr":0.215139,"loss":4.056543,"eta":"0h0m"},{"progress":58.8,"words":2920,"lr":0.205817,"loss":4.03259,"eta":"0h0m"},{"progress":61.5,"words":3046,"lr":0.192411,"loss":3.980249,"eta":"0h0m"},{"progress":64.2,"words":3175,"lr":0.178891,"loss":3.914494,"eta":"0h0m"},{"progress":66,"words":3262,"lr":0.170031,"loss":3.905593,"eta":"0h0m"},{"progress":67.8,"words":3345,"lr":0.161171,"loss":3.912257,"eta":"0h0m"},
 {"progress":69.4,"words":3425,"lr":0.152928,"loss":3.917797,"eta":"0h0m"},
{"progress":71,"words":3499,"lr":0.145031,"loss":3.922638,"eta":"0h0m"},{"progress":72.8,"words":3587,"lr":0.136055,"loss":3.927278,"eta":"0h0m"},
 {"progress":75.4,"words":3714,"lr":0.123112,"loss":3.932528,"eta":"0h0m"},{"progress":77.1,"words":3799,"lr":0.114638,"loss":3.919754,"eta":"0h0m"},{"progress":78.9,"words":3885,"lr":0.105701,"loss":3.877759,"eta":"0h0m"}
]

我希望将[{1}}或y值表示为lr,同时拥有数据对象的loss x值,但我不知道如何在绘制两个轴的同时保留动画。

[UPDATE] 第一次尝试是以下

&#13;
&#13;
progress
&#13;
function displayGraph(id, data, width, height, interpolation, animate, updateDelay, transitionDelay) {

  var margin = {
      top: 30,
      right: 20,
      bottom: 30,
      left: 30
    },
    width = width - margin.left - margin.right,
    height = height - margin.top - margin.bottom;

  var graph = d3.select(id)
    .append("svg:svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var x = d3.scale.linear().domain([0, data.length]).range([-data.length, width]);
  var y = d3.scale.linear().domain([0, d3.max(data, function(d) {
    return d.lr
  })]).range([height, 0]);
  var y2 = d3.scale.linear().domain([0, d3.max(data, function(d) {
    return d.loss
  })]).range([height, 0]);


  var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(10);
  var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(10);

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

  graph.append("svg:path").attr("d", line(data)).attr('stroke', function(d) {
    return "blue"
  });
  graph.append("g") // Add the X Axis
    .attr('stroke', function(d) {
      return "steelblue"
    })
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);
  graph.append("g") // Add the Y Axis
    .attr('stroke', function(d) {
      return "steelblue"
    })
    .attr("class", "y axis")
    .call(yAxis);


  function redrawWithAnimation() {
    graph.selectAll("path")
      .data([data])
      .attr("transform", "translate(" + x(1) + ")")
      .attr("d", line)
      .transition()
      .ease("linear")
      .duration(transitionDelay)
      .attr("transform", "translate(" + x(0) + ")");
  }

  function redrawWithoutAnimation() {
    graph.selectAll("path")
      .data([data])
      .attr("d", line);
  }
  setInterval(function() {
    if (animate) {
      redrawWithAnimation();
    } else {
      redrawWithoutAnimation();
    }
  }, updateDelay);
} //displayGraph
var data = [];
var dataIn = [{
    "progress": 42.3,
    "words": 2116,
    "lr": 0.288598,
    "loss": 4.07032,
    "eta": "0h0m"
  }, {
    "progress": 44,
    "words": 2197,
    "lr": 0.279892,
    "loss": 4.06091,
    "eta": "0h0m"
  }, {
    "progress": 45.7,
    "words": 2279,
    "lr": 0.27161,
    "loss": 4.053332,
    "eta": "0h0m"
  }, {
    "progress": 46.6,
    "words": 2364,
    "lr": 0.267103,
    "loss": 4.052618,
    "eta": "0h0m"
  }, {
    "progress": 49.1,
    "words": 2449,
    "lr": 0.254353,
    "loss": 4.055149,
    "eta": "0h0m"
  }, {
    "progress": 50.9,
    "words": 2532,
    "lr": 0.245493,
    "loss": 4.057263,
    "eta": "0h0m"
  }, {
    "progress": 52.7,
    "words": 2617,
    "lr": 0.236479,
    "loss": 4.059458,
    "eta": "0h0m"
  }, {
    "progress": 57,
    "words": 2833,
    "lr": 0.215139,
    "loss": 4.056543,
    "eta": "0h0m"
  }, {
    "progress": 58.8,
    "words": 2920,
    "lr": 0.205817,
    "loss": 4.03259,
    "eta": "0h0m"
  }, {
    "progress": 61.5,
    "words": 3046,
    "lr": 0.192411,
    "loss": 3.980249,
    "eta": "0h0m"
  }, {
    "progress": 64.2,
    "words": 3175,
    "lr": 0.178891,
    "loss": 3.914494,
    "eta": "0h0m"
  }, {
    "progress": 66,
    "words": 3262,
    "lr": 0.170031,
    "loss": 3.905593,
    "eta": "0h0m"
  }, {
    "progress": 67.8,
    "words": 3345,
    "lr": 0.161171,
    "loss": 3.912257,
    "eta": "0h0m"
  },
  {
    "progress": 69.4,
    "words": 3425,
    "lr": 0.152928,
    "loss": 3.917797,
    "eta": "0h0m"
  },
  {
    "progress": 71,
    "words": 3499,
    "lr": 0.145031,
    "loss": 3.922638,
    "eta": "0h0m"
  }, {
    "progress": 72.8,
    "words": 3587,
    "lr": 0.136055,
    "loss": 3.927278,
    "eta": "0h0m"
  },
  {
    "progress": 75.4,
    "words": 3714,
    "lr": 0.123112,
    "loss": 3.932528,
    "eta": "0h0m"
  }, {
    "progress": 77.1,
    "words": 3799,
    "lr": 0.114638,
    "loss": 3.919754,
    "eta": "0h0m"
  }, {
    "progress": 78.9,
    "words": 3885,
    "lr": 0.105701,
    "loss": 3.877759,
    "eta": "0h0m"
  }
]
// display
displayGraph("#graph1", dataIn, 600, 200, "basis", true, 750, 1500); //linear

// update data
setInterval(function() {
  var v = dataIn.shift();
  if (v) dataIn.push(v);
}, 1000);
&#13;
path {
  /*stroke: steelblue;*/
  stroke-width: 1;
  fill: none;
}
&#13;
&#13;
&#13;

这里有几个问题

  • 我想将新数据点到点,即我想做以下

    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
    <div id="graph1" class="aGraph" style="width:600px; height:200px;"></div>

所以setInterval(function() { var v = dataIn.shift(); if(v) data.push(v); }, 1000); 将获得一个新值来模拟来自数据源的传入数据;

  • 因此折线图应该翻译一个点(就像现在一样),但不要重复&#34; old&#34;范围内的值;
  • 我不知道为什么data轴没有标签,而y轴正在做;

[ATTEMPT 3] 在最后一次测试中,我遵循了建议,将图表添加到组中,以便翻译正常工作并显示x轴标签。我还在数据更新中添加了额外的y。这修复了重绘图表时的上一个问题:

domain

我可以在每次更新时向x.domain([0, 100]); // max(x) is 100 y.domain([0, d3.max(data, function(d) { return d.lr; })]); 数组添加一个新点:

data

我的最后一个问题是// update data setInterval(function() { var v = dataIn.shift(); if (v) data.push(v); }, 1000); 轴上的比例似乎仍然是错误的,因为我希望与y轴进度成比例{{1} }值,但我不知道如何。

&#13;
&#13;
y
&#13;
data.progress
&#13;
function displayGraph(id, data, width, height, interpolation, animate, updateDelay, transitionDelay) {

  var margin = {
      top: 30,
      right: 20,
      bottom: 30,
      left: 30
    },
    width = width - margin.left - margin.right,
    height = height - margin.top - margin.bottom;


var svg = d3.select(id)
  .append("svg:svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
var graph = svg.append('g')
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  
  var x = d3.scale.linear().domain([0, 100]).range([0, width]); // max(x) is 100
  var y = d3.scale.linear().domain([0, 1]).range([height, 0]); // max(y) is 1   
  var line = d3.svg.line()
    .x(function(d, i) {
      return x(i);
    })
    .y(function(d) {
      return y(d.lr);
    })
    .interpolate(interpolation)

  var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(10);
  var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(10);

  graph.append("svg:path")
    .attr("d", line(data))
    .attr('stroke', function(d) {
      return "blue"
    });
  graph.append("g") // Add the X Axis
    .attr('stroke', function(d) {
      return "steelblue"
    })
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);
  graph.append("g") // Add the Y Axis
    .attr('stroke', function(d) {
      return "steelblue"
    })
    .attr("class", "y axis")
    .call(yAxis);


  function redrawWithAnimation() {
    //x.domain(d3.extent(data, function(d,i) { return i; }));
    x.domain([0, 100]); // max(x) is 100
    y.domain([0, d3.max(data, function(d) {
      return d.lr;
    })]);
    graph.selectAll("path")
      .data([data])
      .attr("transform", "translate(" + x(1) + ")")
      .attr("d", line)
      .transition()
      .ease("linear")
      .duration(transitionDelay)
      .attr("transform", "translate(" + x(0) + ")");

  }

  function redrawWithoutAnimation() {
    // static update without animation
    graph.selectAll("path")
      .data([data]) // set the new data
      .attr("d", line); // apply the new data values
  }
  setInterval(function() {
    if (animate) {
      redrawWithAnimation();
    } else {
      redrawWithoutAnimation();
    }
  }, updateDelay);
} //displayGraph
var data = [];
var dataIn = [{
    "progress": 42.3,
    "words": 2116,
    "lr": 0.288598,
    "loss": 4.07032,
    "eta": "0h0m"
  }, {
    "progress": 44,
    "words": 2197,
    "lr": 0.279892,
    "loss": 4.06091,
    "eta": "0h0m"
  }, {
    "progress": 45.7,
    "words": 2279,
    "lr": 0.27161,
    "loss": 4.053332,
    "eta": "0h0m"
  }, {
    "progress": 46.6,
    "words": 2364,
    "lr": 0.267103,
    "loss": 4.052618,
    "eta": "0h0m"
  }, {
    "progress": 49.1,
    "words": 2449,
    "lr": 0.254353,
    "loss": 4.055149,
    "eta": "0h0m"
  }, {
    "progress": 50.9,
    "words": 2532,
    "lr": 0.245493,
    "loss": 4.057263,
    "eta": "0h0m"
  }, {
    "progress": 52.7,
    "words": 2617,
    "lr": 0.236479,
    "loss": 4.059458,
    "eta": "0h0m"
  }, {
    "progress": 57,
    "words": 2833,
    "lr": 0.215139,
    "loss": 4.056543,
    "eta": "0h0m"
  }, {
    "progress": 58.8,
    "words": 2920,
    "lr": 0.205817,
    "loss": 4.03259,
    "eta": "0h0m"
  }, {
    "progress": 61.5,
    "words": 3046,
    "lr": 0.192411,
    "loss": 3.980249,
    "eta": "0h0m"
  }, {
    "progress": 64.2,
    "words": 3175,
    "lr": 0.178891,
    "loss": 3.914494,
    "eta": "0h0m"
  }, {
    "progress": 66,
    "words": 3262,
    "lr": 0.170031,
    "loss": 3.905593,
    "eta": "0h0m"
  }, {
    "progress": 67.8,
    "words": 3345,
    "lr": 0.161171,
    "loss": 3.912257,
    "eta": "0h0m"
  },
  {
    "progress": 69.4,
    "words": 3425,
    "lr": 0.152928,
    "loss": 3.917797,
    "eta": "0h0m"
  },
  {
    "progress": 71,
    "words": 3499,
    "lr": 0.145031,
    "loss": 3.922638,
    "eta": "0h0m"
  }, {
    "progress": 72.8,
    "words": 3587,
    "lr": 0.136055,
    "loss": 3.927278,
    "eta": "0h0m"
  },
  {
    "progress": 75.4,
    "words": 3714,
    "lr": 0.123112,
    "loss": 3.932528,
    "eta": "0h0m"
  }, {
    "progress": 77.1,
    "words": 3799,
    "lr": 0.114638,
    "loss": 3.919754,
    "eta": "0h0m"
  }, {
    "progress": 78.9,
    "words": 3885,
    "lr": 0.105701,
    "loss": 3.877759,
    "eta": "0h0m"
  }
]
// display
displayGraph("#graph1", data, 600, 200, "basis", true, 1000, 1000); //linear

// update data
setInterval(function() {
  var v = dataIn.shift();
  if (v) data.push(v);
}, 1000);
&#13;
&#13;
&#13;

另一个问题是,鉴于此代码,path { /*stroke: steelblue;*/ stroke-width: 1; fill: none; }的范围不会随新值一起延伸,因此我会从图表中获取<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> <div id="graph1" class="aGraph" style="width:600px; height:200px;"></div>值:

enter image description here

1 个答案:

答案 0 :(得分:1)

你的y轴不显示的原因是因为它在svg之外。如果您将一个组添加到svg并按边距转换它将会显示。

var svg = d3.select(id)
  .append("svg:svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
var graph = svg.append('g')
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

您能否澄清一下目前数据添加方式的问题?看起来很好。