如何在d3 js中添加带状色带?

时间:2018-05-22 02:53:09

标签: d3.js

基于Simple graph with grid lines in v4(例如),我想在网格线之间添加系带,类似于下图:

enter image description here

我怎么能实现这个目标?

感谢。

2 个答案:

答案 0 :(得分:2)

我能够使用rect元素解决此问题,并根据opacity启用index。首先,我将yaxis的所有值都放入一个数组中,这样我就可以为每个y获取rect属性,然后将rects添加到一个组g中1}}。

// add the Y Axis
svg.append("g").attr('class', 'y axis')
  .call(d3.axisLeft(y));

//Store all values of the y-axis to an array
var yval = []
d3.selectAll('.y.axis>g>text').each(function(d) {
  yval.push(d);
});

// Create rects and assign opacity based on index
rects.selectAll('rect').data(yval).enter().append('rect')
     .attr('x', 0).attr('y', function(d) { return y(d); })
     .attr('height', height / yval.length)
     .attr('width', width).style('fill-opacity', function(d, i) {
         if (i == 0) {
            return 0;
         }
         if (i % 2 == 0) {
            return 0.1;
         } else {
            return 0;
         }
     });



// set the dimensions and margins of the graph
var margin = {
    top: 20,
    right: 20,
    bottom: 30,
    left: 50
  },
  width = 960 - margin.left - margin.right,
  height = 500 - margin.top - margin.bottom;

// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");

// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);

// define the line
var valueline = d3.line()
  .x(function(d) {
    return x(d.date);
  })
  .y(function(d) {
    return y(d.close);
  });

// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
  .attr("width", width + margin.left + margin.right)
  .attr("height", height + margin.top + margin.bottom)
  .append("g")
  .attr("transform",
    "translate(" + margin.left + "," + margin.top + ")");

// gridlines in x axis function
function make_x_gridlines() {
  return d3.axisBottom(x)
    .ticks(5)
}

// gridlines in y axis function
function make_y_gridlines() {
  return d3.axisLeft(y)
    .ticks(5)
}

// Get the data
var data = [{
    "date": "1-May-12",
    "close": 58.13
  },
  {
    "date": "30-Apr-12",
    "close": 53.98
  },
  {
    "date": "27-Apr-12",
    "close": 67
  },
  {
    "date": "26-Apr-12",
    "close": 89.7
  },
  {
    "date": "25-Apr-12",
    "close": 99
  },
  {
    "date": "24-Apr-12",
    "close": 130.28
  },
  {
    "date": "23-Apr-12",
    "close": 166.7
  },
  {
    "date": "20-Apr-12",
    "close": 234.98
  },
  {
    "date": "19-Apr-12",
    "close": 345.44
  },
  {
    "date": "18-Apr-12",
    "close": 443.34
  },
  {
    "date": "17-Apr-12",
    "close": 543.7
  },
  {
    "date": "16-Apr-12",
    "close": 580.13
  },
  {
    "date": "13-Apr-12",
    "close": 605.23
  },
  {
    "date": "12-Apr-12",
    "close": 622.77
  },
  {
    "date": "11-Apr-12",
    "close": 626.2
  },
  {
    "date": "10-Apr-12",
    "close": 628.44
  },
  {
    "date": "9-Apr-12",
    "close": 636.23
  },
  {
    "date": "5-Apr-12",
    "close": 633.68
  },
  {
    "date": "4-Apr-12",
    "close": 624.31
  },
  {
    "date": "3-Apr-12",
    "close": 629.32
  },
  {
    "date": "2-Apr-12",
    "close": 618.63
  },
  {
    "date": "30-Mar-12",
    "close": 599.55
  },
  {
    "date": "29-Mar-12",
    "close": 609.86
  },
  {
    "date": "28-Mar-12",
    "close": 617.62
  },
  {
    "date": "27-Mar-12",
    "close": 614.48
  },
  {
    "date": "26-Mar-12",
    "close": 606.98
  }
]


// format the data
data.forEach(function(d) {
  d.date = parseTime(d.date);
  d.close = +d.close;
});

// Scale the range of the data
x.domain(d3.extent(data, function(d) {
  return d.date;
}));
y.domain([0, d3.max(data, function(d) {
  return d.close;
})]);

// add the X gridlines
svg.append("g")
  .attr("class", "grid")
  .attr("transform", "translate(0," + height + ")")
  .call(make_x_gridlines()
    .tickSize(-height)
    .tickFormat("")
  )

// add the Y gridlines
svg.append("g")
  .attr("class", "grid")
  .call(make_y_gridlines()
    .tickSize(-width)
    .tickFormat("")
  )

var rects = svg.append('g').attr('class', 'intBands')

// add the valueline path.
svg.append("path")
  .data([data])
  .attr("class", "line")
  .attr("d", valueline);

// add the X Axis
svg.append("g")
  .attr("transform", "translate(0," + height + ")")
  .call(d3.axisBottom(x));

// add the Y Axis
svg.append("g").attr('class', 'y axis')
  .call(d3.axisLeft(y));


var yval = []
d3.selectAll('.y.axis>g>text').each(function(d) {
  yval.push(d);
});


rects.selectAll('rect').data(yval).enter().append('rect')
  .attr('x', 0).attr('y', function(d) {
    return y(d)
  }).attr('height', height / yval.length).attr('width', width).style('fill-opacity', function(d, i) {
    if (i == 0) {
      return 0;
    }
    if (i % 2 == 0) {
      return 0.1;
    } else {
      return 0;
    }

  });

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}

.grid line {
  stroke: lightgrey;
  stroke-opacity: 0.7;
  shape-rendering: crispEdges;
}

.grid path {
  stroke-width: 0;
}

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  /* set the CSS */
</style>

<body>

  <!-- load the d3.js library -->
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script>
  </script>
</body>
&#13;
&#13;
&#13;

如果有人有更好的方法,请发布您的解决方案。感谢。

答案 1 :(得分:1)

一种方法是从y轴获取网格线之间的范围,并为每个网格线包含一个矩形:

&#13;
&#13;
// set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

// parse the date / time
var parseTime = d3.timeParse("%d-%b-%y");

// set the ranges
var x = d3.scaleTime().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);

// define the line
var valueline = d3.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform",
          "translate(" + margin.left + "," + margin.top + ")");

// gridlines in x axis function
function make_x_gridlines() {   
    return d3.axisBottom(x)
        .ticks(5)
}

// gridlines in y axis function
function make_y_gridlines() {   
    return d3.axisLeft(y)
        .ticks(5)
}

// Get the data
var data = [
  { date: "1-May-12", close: 58.13 },
  { date: "30-Apr-12", close: 53.98 },
  { date: "27-Apr-12", close: 67.00 },
  { date: "26-Apr-12", close: 89.70 },
  { date: "25-Apr-12", close: 99.00 },
  { date: "24-Apr-12", close: 130.28 },
  { date: "23-Apr-12", close: 166.70 },
  { date: "20-Apr-12", close: 234.98 },
  { date: "19-Apr-12", close: 345.44 },
  { date: "18-Apr-12", close: 443.34 },
  { date: "17-Apr-12", close: 543.70 },
  { date: "16-Apr-12", close: 580.13 },
  { date: "13-Apr-12", close: 605.23 },
  { date: "12-Apr-12", close: 622.77 },
  { date: "11-Apr-12", close: 626.20 },
  { date: "10-Apr-12", close: 628.44 },
  { date: "9-Apr-12", close: 636.23 },
  { date: "5-Apr-12", close: 633.68 },
  { date: "4-Apr-12", close: 624.31 },
  { date: "3-Apr-12", close: 629.32 },
  { date: "2-Apr-12", close: 618.63 },
  { date: "30-Mar-12", close: 599.55 },
  { date: "29-Mar-12", close: 609.86 },
  { date: "28-Mar-12", close: 617.62 },
  { date: "27-Mar-12", close: 614.48 },
  { date: "26-Mar-12", close: 606.98 }
]
// d3.csv("data.csv").then(data) {

  // format the data
  data.forEach(function(d) {
      d.date = parseTime(d.date);
      d.close = +d.close;
  });

  // Scale the range of the data
  x.domain(d3.extent(data, function(d) { return d.date; }));
  y.domain([0, d3.max(data, function(d) { return d.close; })]);

  // add the X gridlines
  svg.append("g")     
      .attr("class", "grid")
      .attr("transform", "translate(0," + height + ")")
      .call(make_x_gridlines()
          .tickSize(-height)
          .tickFormat("")
      )

  // add the Y gridlines
  svg.append("g")     
      .attr("class", "grid ylines")
      .call(make_y_gridlines()
          .tickSize(-width)
          .tickFormat("")
      )

  // add the valueline path.
  svg.append("path")
      .data([data])
      .attr("class", "line")
      .attr("d", valueline);

  // add the X Axis
  svg.append("g")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x));

  // add the Y Axis
  svg.append("g")
    .call(d3.axisLeft(y));

  var bands = [];
  var start;
  d3.selectAll(".ylines .tick line")
    .each(function(d, i) {
      if (i % 2 == 0)
        start = d;
      else {
        bands.push({ "start": start, "end": d })
        start = null;
      }
    });
  // If it remains the top band:
  if (start)
    bands.push({ "start": start, "end": d3.max(data, function(d) { return d.close; }) })

  svg.append("g").selectAll("band")
    .data(bands)
    .enter().append("rect")
    .attr("y", d => y(d.end))
    .attr("height", d => y(d.start) - y(d.end))
    .attr("x", d => 0)
    .attr("width", d => width)
    .style("opacity", 0.1)
    .style("stroke", "#005e23")
    .style("fill", "#005e23");

// });
&#13;
.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}

.grid line {
  stroke: lightgrey;
  stroke-opacity: 0.7;
  shape-rendering: crispEdges;
}

.grid path {
  stroke-width: 0;
}
&#13;
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
</body>
&#13;
&#13;
&#13;

// Let's retrieve from the grid lines the associated bands y-extremities:
var bands = [];
var start;
d3.selectAll(".ylines .tick line")
  .each(function(d, i) {
    if (i % 2 == 0)
      start = d;
    else {
      bands.push({ "start": start, "end": d })
      start = null; // in order to know if we should use the top band
    }
  });
// If it remains a top band:
if (start)
  bands.push({ "start": start, "end": d3.max(data, function(d) { return d.close; }) })

// Our bands look like:
// [{start: 0,end: 100}, {start: 200,end: 300}, ..., {start: 600,end: 636.23}]
svg.append("g").selectAll("band")
  .data(bands)
  .enter().append("rect")
  .attr("y", d => y(d.end))
  .attr("height", d => y(d.start) - y(d.end))
  .attr("x", d => 0)
  .attr("width", d => width)
  .style("opacity", 0.1)
  .style("stroke", "#005e23")
  .style("fill", "#005e23");

使用网格线而不是刻度线可以选择比网格线更多的刻度。