当前位置: 动力学知识库 > 问答 > 编程问答 >

javascript - Update axes on D3.JS scrollable line chart based on user's view

问题描述:

I'm trying to get a D3.JS scrollable line chart to automatically readjust the axes based on the user's current view.

To give an example, the intended behaviour can be seen on Google Finance.

In the first chart, you can see the price peaking at around 5485:

In the second chart, I have scrolled forward and you can see that the chart has automatically re-scaled to accommodate the new peak at 5510:

Note that this behaviour is different to simply making the chart responsive, which can easily be achieved by putting the entire chart, including data outside the user's view, in a responsive container, e.g.:

.svg-container {

display: inline-block;

position: relative;

width: 100%;

padding-bottom: 65%;

vertical-align: top;

overflow: hidden;

}

In order to solve this problem, I would need to distinguish between the entirety of the price history and the segment the user is currently viewing, then set y-axis range. How can I achieve this?

网友答案:

Not sure I understand the question, but it seems like you just want to scale the y-axis as the selected data range changes. Building off this classic example, I've modified it so that as you brush or zoom the y-axis domain changes to match the current selection. The key code is this in the brushed callback:

// set new y domain based on selection
y.domain([0, d3.max(data, function(d) {
  if (d.x < x.domain()[0] || d.x > x.domain()[1]) return 0;
  else return d.y;
})]);
// redraw y axis
focus.select(".axis--y").call(yAxis);

Complete example:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  .area {
    fill: steelblue;
    clip-path: url(#clip);
  }
  
  .zoom {
    cursor: move;
    fill: none;
    pointer-events: all;
  }
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
  var svg = d3.select("svg"),
    margin = {
      top: 20,
      right: 20,
      bottom: 110,
      left: 40
    },
    margin2 = {
      top: 430,
      right: 20,
      bottom: 30,
      left: 40
    },
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom,
    height2 = +svg.attr("height") - margin2.top - margin2.bottom;

  var x = d3.scaleLinear().range([0, width]),
    x2 = d3.scaleLinear().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    y2 = d3.scaleLinear().range([height2, 0]);

  var xAxis = d3.axisBottom(x),
    xAxis2 = d3.axisBottom(x2),
    yAxis = d3.axisLeft(y);

  var brush = d3.brushX()
    .extent([
      [0, 0],
      [width, height2]
    ])
    .on("brush end", brushed);

  var zoom = d3.zoom()
    .scaleExtent([1, Infinity])
    .translateExtent([
      [0, 0],
      [width, height]
    ])
    .extent([
      [0, 0],
      [width, height]
    ])
    .on("zoom", zoomed);

  var area = d3.area()
    .curve(d3.curveMonotoneX)
    .x(function(d) {
      return x(d.x);
    })
    .y0(height)
    .y1(function(d) {
      return y(d.y);
    });

  var area2 = d3.area()
    .curve(d3.curveMonotoneX)
    .x(function(d) {
      return x2(d.x);
    })
    .y0(height2)
    .y1(function(d) {
      return y2(d.y);
    });

  svg.append("defs").append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

  var focus = svg.append("g")
    .attr("class", "focus")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var context = svg.append("g")
    .attr("class", "context")
    .attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");

  var data = [];
  for (var i = 0; i < 100; i++) {
    data.push({
      x: i,
      y: i > 50 ? Math.random() * 100 : Math.random() * 50
    });
  }

  x.domain(d3.extent(data, function(d) {
    return d.x;
  }));
  y.domain([0, d3.max(data, function(d) {
    return d.y;
  })]);
  x2.domain(x.domain());
  y2.domain(y.domain());

  focus.append("path")
    .datum(data)
    .attr("class", "area")
    .attr("d", area);

  focus.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

  focus.append("g")
    .attr("class", "axis axis--y")
    .call(yAxis);

  context.append("path")
    .datum(data)
    .attr("class", "area")
    .attr("d", area2);

  context.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + height2 + ")")
    .call(xAxis2);

  context.append("g")
    .attr("class", "brush")
    .call(brush)
    .call(brush.move, x.range());

  svg.append("rect")
    .attr("class", "zoom")
    .attr("width", width)
    .attr("height", height)
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .call(zoom);

  function brushed() {
    if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
    var s = d3.event.selection || x2.range();
    x.domain(s.map(x2.invert, x2));
    focus.select(".area").attr("d", area);
    focus.select(".axis--x").call(xAxis);

    y.domain([0, d3.max(data, function(d) {
      if (d.x < x.domain()[0] || d.x > x.domain()[1]) return 0;
      else return d.y;
    })]);
    focus.select(".axis--y").call(yAxis);

    svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
      .scale(width / (s[1] - s[0]))
      .translate(-s[0], 0));
  }

  function zoomed() {
    if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
    var t = d3.event.transform;
    x.domain(t.rescaleX(x2).domain());
    focus.select(".area").attr("d", area);
    focus.select(".axis--x").call(xAxis);

    y.domain([0, d3.max(data, function(d) {
      if (d.x < x.domain()[0] || d.x > x.domain()[1]) return 0;
      else return d.y;
    })]);
    focus.select(".axis--y").call(yAxis);

    context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
  }
</script>
分享给朋友:
您可能感兴趣的文章:
随机阅读: