0

我正在尝试使用 D3 创建日历热图,与 Github 贡献日历非常相似。

我无法正确对齐星期几。它似乎每个月都会重复,并且没有正确的边距或对齐方式。我只希望在日历左侧显示一次。

像这样:

在此处输入图像描述

这是我的样子:

在此处输入图像描述

这是我的代码:

<style>
#calendar {
  margin: 20px;
}
.month {
  margin-right: 8px;
}
.month-name {
  font-size: 85%;
  fill: #777;
  font-family: Muli, san-serif;
}
.day.hover {
  stroke: #6d6E70;
  stroke-width: 2;
}
.day.focus {
  stroke: #ffff33;
  stroke-width: 2;
}
</style>
<div style="text-align:center;" id="calendar"></div>

<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>

function drawCalendar(dateData){

  var weeksInMonth = function(month){
    var m = d3.timeMonth.floor(month)
    return d3.timeWeeks(d3.timeWeek.floor(m), d3.timeMonth.offset(m,1)).length;
  }

  //var minDate = new Date(2018, 12, 31);
  var minDate = d3.min(dateData, function(d) { return new Date(2018, 12, 1 ) });
  //var minDate = d3.min(dateData, function(d) { return new Date(d.day) });
  console.log(minDate);
  //var maxDate = new Date(2019, 11, 30);
  var maxDate = d3.max(dateData, function(d) { return new Date(2019, 11, 30 ) });
  console.log(maxDate);

  var cellMargin = 2,
      calY=10,//offset of calendar in each group
      xOffset=-5,
      dayName = ['Su','Mo','Tu','We','Th','Fr','Sa'],
      cellSize = 20;

  var day = d3.timeFormat("%w"),
      week = d3.timeFormat("%U"),
      format = d3.timeFormat("%Y-%m-%d"),
      titleFormat = d3.utcFormat("%a, %d-%b"),
      monthName = d3.timeFormat("%B"),
      months= d3.timeMonth.range(d3.timeMonth.floor(minDate), maxDate);

  var svg = d3.select("#calendar").selectAll("svg")
    .data(months)
    .enter().append("svg")
    .attr("class", "month")
    .attr("height", ((cellSize * 7) + (cellMargin * 8) + 20) ) // the 20 is for the month labels
    .attr("width", function(d) {
      var columns = weeksInMonth(d);
      return ((cellSize * columns) + (cellMargin * (columns + 1)));
    })
    .append("g")


  svg.append("text")
    .attr("class", "month-name")
    .attr("y", (cellSize * 7) + (cellMargin * 8) + 15 )
    .attr("x", function(d) {
      var columns = weeksInMonth(d);
      return (((cellSize * columns) + (cellMargin * (columns + 1))) / 2);
    })
    .attr("text-anchor", "middle")
    .text(function(d) { return monthName(d); })


 //create day labels
        var days = ['Su','Mo','Tu','We','Th','Fr','Sa'];
        var dayLabels=svg.append("g").attr("id","dayLabels")
        days.forEach(function(d,i)    {
            dayLabels.append("text")
            .attr("class","dayLabel")
            .attr("x",xOffset)
            .attr("y",function(d) { return calY+(i * cellSize); })
            .text(d);
        })



  var rect = svg.selectAll("rect.day")
    .data(function(d, i) { return d3.timeDays(d, new Date(d.getFullYear(), d.getMonth()+1, 1)); })
    .enter().append("rect")
    .attr("class", "day")
    .attr("width", cellSize)
    .attr("height", cellSize)
    .attr("rx", 3).attr("ry", 3) // rounded corners
    .attr("fill", '#eaeaea') // default light grey fill
    .attr("y", function(d) { return (day(d) * cellSize) + (day(d) * cellMargin) + cellMargin; })
    .attr("x", function(d) { return ((week(d) - week(new Date(d.getFullYear(),d.getMonth(),1))) * cellSize) + ((week(d) - week(new Date(d.getFullYear(),d.getMonth(),1))) * cellMargin) + cellMargin ; })
    .on("mouseover", function(d) {
      d3.select(this).classed('hover', true);
    })
    .on("mouseout", function(d) {
      d3.select(this).classed('hover', false);
    })
    .datum(format);

  rect.append("title")
    .text(function(d) { return titleFormat(new Date(d)); });

  var lookup = d3.nest()
    .key(function(d) { return d.day; })
    .rollup(function(leaves) {
      return d3.sum(leaves, function(d){ return parseInt(d.count); });
    })
    .object(dateData);

  var scale = d3.scaleLinear()
    .domain(d3.extent(dateData, function(d) { return parseInt(d.count); }))
    .range([0.2,1]); // the interpolate used for color expects a number in the range [0,1] but i don't want the lightest part of the color scheme

  rect.filter(function(d) { return d in lookup; })
    .style("fill", function(d) { return d3.interpolateYlGn(scale(lookup[d])); })
    .select("title")
    .text(function(d) { return titleFormat(new Date(d)) + ":  " + lookup[d]; });

}

d3.csv("dates.csv", function(response){
  drawCalendar(response);
})


</script>

还有一个包含以下值的输入 csv 文件:

day,count
2019-05-12,171
2019-06-17,139
2019-05-02,556
2019-04-10,1
2019-05-04,485
2019-03-27,1
2019-05-26,42
2019-05-25,337
2019-05-23,267
2019-05-05,569
2019-03-31,32
2019-03-25,128
2019-05-13,221
2019-03-30,26
2019-03-15,3
2019-04-24,10
2019-04-27,312
2019-03-20,99
2019-05-10,358
2019-04-01,15
2019-05-11,199
2019-07-06,744
2019-05-08,23
2019-03-28,98
2019-03-29,64
2019-04-30,152
2019-03-21,148
2019-03-19,20
2019-05-07,69
2019-04-29,431
2019-04-25,330
2019-04-28,353
2019-04-18,9
2019-01-10,1
2019-01-09,2
2019-03-26,21
2019-05-27,18
2019-04-19,10
2019-04-06,1
2019-04-12,214
2019-05-03,536
2019-07-03,3
2019-06-16,1
2019-03-24,138
2019-04-26,351
2019-04-23,14
2019-05-01,19
2019-07-05,523
2019-05-22,3
2019-05-09,430
2019-05-24,472
2019-04-11,172
2019-03-17,7
2019-05-14,10
2019-05-06,449
2019-07-04,295
2019-05-15,12
2019-03-23,216
2019-03-18,47
2019-03-22,179
4

1 回答 1

0

通常,您在 SVG 中允许留有余量,如下所示:

  const margin = { top: 10, right: 20, bottom: 10, left: 5 }
  const svg = d3
      .select('#chart')
      .append('svg')
      .attr('width', 900 + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom)
      .append('g')
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')

基本上,您创建一个大于绘图区域的 SVG 元素,然后将图表移动(平移)到边距。然后你的轴可以出现在边缘

于 2019-11-26T00:16:52.947 回答