将矩形附加到折线图以显示周末

时间:2017-02-09 10:20:52

标签: javascript d3.js

我正在学习如何用D3绘制图形,我想在图形上绘制一个或类似的区域,以表明周末的日期与其余的不同(我有附件和图像来解释自己更好) )。我现在所拥有的是我后来展示的小提琴。

正如您所看到的,在数据​​上,有些日子没有值,并且缺少各自的日期。

JSFiddle我现在所接受的:

JSFiddle

default_controller

CSS:

var data = [
  {"date":"1-May-13","close":58.13},
  {"date":"30-Apr-13","close":53.98},
  {"date":"27-Apr-13","close":67.00},
  {"date":"26-Apr-13","close":89.70},
  {"date":"25-Apr-13","close":99.00},
  {"date":"24-Apr-13","close":130.28},
  {"date":"23-Apr-13","close":166.70},
  {"date":"20-Apr-13","close":234.98},
  {"date":"19-Apr-13","close":345.44},
  {"date":"18-Apr-13","close":443.34},
];

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

var parseDate = d3.time.format("%d-%b-%y").parse,
    bisectDate = d3.bisector(function(d) { return d.date; }).left,
    formatValue = d3.format(",.2f"),
    formatCurrency = function(d) { return "$" + formatValue(d); };

var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var line = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });

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 + ")");

  data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.close = +d.close;

  data.sort(function(a, b) {
    return a.date - b.date;
  });

  x.domain([data[0].date, data[data.length - 1].date]);
  y.domain(d3.extent(data, function(d) { return d.close; }));

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

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Price ($)");

  svg.append("path")
      .datum(data)
      .attr("class", "line")
      .attr("d", line);

  var focus = svg.append("g")
      .attr("class", "focus")
      .style("display", "none");

  focus.append("circle")
      .attr("r", 4.5);

  focus.append("text")
      .attr("x", 9)
      .attr("dy", ".35em");

  svg.append("rect")
      .attr("class", "overlay")
      .attr("width", width)
      .attr("height", height)
      .on("mouseover", function() { focus.style("display", null); })
      .on("mouseout", function() { focus.style("display", "none"); })
      .on("mousemove", mousemove);

  function mousemove() {
    var x0 = x.invert(d3.mouse(this)[0]),
        i = bisectDate(data, x0, 1),
        d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.date > d1.date - x0 ? d1 : d0;
    focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")");
    focus.select("text").text(formatCurrency(d.close));
  }
});

我想要完成的图像:

Graph with weekend

1 个答案:

答案 0 :(得分:1)

给出你的x比例域,可以用:

更好地定义
x.domain(d3.extent(data, function(d) {
    return d.date;
}))

我们可以在第一个日期(x.domain()[0])和最后一个日期(x.domain()[1])之间的所有星期六填充数组。我们只需要找到星期六:酒吧的宽度将是星期六+ 2天(这将涵盖整个星期六和整个星期天)。

有几个功能可以做到这一点,这里有一个:

function findSat(date1, date2) {
    if (date1 > date2) return false;
    var saturdays = [];
    while (date1 < date2) {
        if (date1.getDay() === 6) saturdays.push(new Date(date1));
        date1.setDate(date1.getDate() + 1);
    }
    return saturdays;
}

使用该功能,我们可以在第一个和最后一个日期之间的所有星期六填充一个数组:

var rectDate = findSat(x.domain()[0], x.domain()[1]);

现在,我们使用rectDate数组来创建矩形:

var rects = svg.selectAll(".rects")
    .data(rectDate)
    .enter()
    .append("rect")
    .attr("y", margin.top)
    .attr("height", height - margin.bottom)
    .attr("x", d => x(d))
    .attr("width", d => x(d3.time.day.offset(d, +2)) - x(d))
    .attr("fill", "yellow");

请注意,对于宽度,我们从星期六减去“星期六+ 2天”:

.attr("width", d => x(d3.time.day.offset(d, +2)) - x(d))

这是您更新的小提琴:http://jsfiddle.net/36yaot6t/

这里是相同的代码,在Stack片段中:

var data = [{
    "date": "1-May-13",
    "close": 58.13
}, {
    "date": "30-Apr-13",
    "close": 53.98
}, {
    "date": "27-Apr-13",
    "close": 67.00
}, {
    "date": "26-Apr-13",
    "close": 89.70
}, {
    "date": "25-Apr-13",
    "close": 99.00
}, {
    "date": "24-Apr-13",
    "close": 130.28
}, {
    "date": "23-Apr-13",
    "close": 166.70
}, {
    "date": "20-Apr-13",
    "close": 234.98
}, {
    "date": "19-Apr-13",
    "close": 345.44
}, {
    "date": "18-Apr-13",
    "close": 443.34
}, ];

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

var parseDate = d3.time.format("%d-%b-%y").parse,
    bisectDate = d3.bisector(function(d) {
        return d.date;
    }).left,
    formatValue = d3.format(",.2f"),
    formatCurrency = function(d) {
        return "$" + formatValue(d);
    };

var x = d3.time.scale()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var line = d3.svg.line()
    .x(function(d) {
        return x(d.date);
    })
    .y(function(d) {
        return y(d.close);
    });

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 + ")");

data.forEach(function(d) {
    d.date = parseDate(d.date);
    d.close = +d.close;
});

data.sort(function(a, b) {
    return a.date - b.date;
});

x.domain(d3.extent(data, function(d) {
    return d.date;
}))
y.domain(d3.extent(data, function(d) {
    return d.close;
}));

function findSat(date1, date2) {
    if (date1 > date2) return false;
    var saturdays = [];
    while (date1 < date2) {
        if (date1.getDay() === 6) saturdays.push(new Date(date1));
        date1.setDate(date1.getDate() + 1);
    }
    return saturdays;
}

var rectDate = findSat(x.domain()[0], x.domain()[1]);

var rects = svg.selectAll(".rects")
    .data(rectDate)
    .enter()
    .append("rect")
    .attr("y", margin.top)
    .attr("height", height - margin.bottom)
    .attr("x", d => x(d))
    .attr("width", d => x(d3.time.day.offset(d, +2)) - x(d))
    .attr("fill", "yellow");

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

svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("Price ($)");

svg.append("path")
    .datum(data)
    .attr("class", "line")
    .attr("d", line);

var focus = svg.append("g")
    .attr("class", "focus")
    .style("display", "none");

focus.append("circle")
    .attr("r", 4.5);

focus.append("text")
    .attr("x", 9)
    .attr("dy", ".35em");

svg.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height)
    .on("mouseover", function() {
        focus.style("display", null);
    })
    .on("mouseout", function() {
        focus.style("display", "none");
    })
    .on("mousemove", mousemove);

function mousemove() {
    var x0 = x.invert(d3.mouse(this)[0]),
        i = bisectDate(data, x0, 1),
        d0 = data[i - 1],
        d1 = data[i],
        d = x0 - d0.date > d1.date - x0 ? d1 : d0;
    focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")");
    focus.select("text").text(formatCurrency(d.close));
}
body {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

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

.overlay {
  fill: none;
  pointer-events: all;
}

.focus circle {
  fill: none;
  stroke: steelblue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>