A simple version of the GiHub commit calendar, just to try to simplify this exemple by using d3.time utilities. if you need this type of calendar, you should know about the cal-heatmap plugin.
Last active
August 27, 2015 13:40
-
-
Save biovisualize/31eb70a0880f66262cda to your computer and use it in GitHub Desktop.
Simple GitHub Calendar
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<style> | |
.cell { | |
stroke: silver; | |
} | |
.label { | |
font-size: 10px; | |
font-family: sans-serif; | |
fill: #666; | |
} | |
.tooltip { | |
border: silver 1px solid; | |
background-color: white; | |
position: absolute; | |
pointer-events: none; | |
border-radius: 8px 8px 0px 8px; | |
padding: 5px; | |
opacity: 0.8; | |
font-size: 10px; | |
font-family: sans-serif; | |
} | |
.hovered { | |
stroke: black; | |
} | |
.cell-hover { | |
pointer-events: none; | |
stroke-width: 2px; | |
stroke: black; | |
} | |
.cell.out-of-range { | |
display: none; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"></div> | |
<div class="tooltip"></div> | |
<script> | |
var firstDayOfTheMonth = new Date('01/01/2015'); | |
var lastDayOfTheMonth = new Date('12/31/2015'); | |
var firstDayOfWeekBeforeFirstDayOfMonth = d3.time.week.offset(firstDayOfTheMonth, -1); | |
var data = d3.time.weeks(firstDayOfWeekBeforeFirstDayOfMonth, lastDayOfTheMonth) | |
.map(function(d){ | |
var date = new Date(d); | |
return d3.time.days(date, d3.time.week.offset(date, 1)).map(function(dB){ return {date: dB, value: ~~(Math.random()*100)}; }); | |
}); | |
var firstIncompleteWeek = d3.time.days(firstDayOfWeekBeforeFirstDayOfMonth, new Date(data[0][0].date)); | |
var lastIncompleteWeek = d3.time.days(lastDayOfTheMonth, new Date(data[data.length-1][data[0].length -1].date)); | |
var dataValues = d3.merge(data).map(function(d){ return d.value; }); | |
// nullify days out of range | |
data[0].forEach(function(d, i){ | |
if(i < data[0].length - firstIncompleteWeek.length){ | |
d.value = null; | |
} | |
}); | |
data[data.length-1].forEach(function(d, i){ | |
if(i > data[0].length - 1 - lastIncompleteWeek.length){ | |
d.value = null; | |
} | |
}); | |
var config = { | |
width: 1000, | |
height: 250, | |
margin: {top: 80, right: 10, bottom: 40, left: 30}, | |
dateFormatX: d3.time.format('%d %b'), | |
dateFormatY: d3.time.format('%a'), | |
dateFormatTooltip: d3.time.format('%d %b, %Y'), | |
labelAxisPadding: 5, | |
colorScale: d3.scale.linear().domain(d3.extent(dataValues)).range(['white', 'skyblue']) | |
}; | |
config.chartWidth = config.width - config.margin.left - config.margin.right; | |
config.chartHeight = config.height - config.margin.top - config.margin.bottom; | |
config.cellWidth = Math.floor(config.chartWidth / data.length); | |
config.cellHeight = ~~(config.chartHeight / data[0].length); | |
var tooltip = d3.select('.tooltip').style({display: 'none'}); | |
var chartContainer = d3.select('.container') | |
.append('svg') | |
.attr({ | |
width: config.width, | |
height: config.height | |
}) | |
.append('g') | |
.attr({ | |
transform: 'translate(' + [config.margin.left, config.margin.top] + ')' | |
}); | |
var columns = chartContainer.selectAll('g.column') | |
.data(data); | |
columns.enter().append('g') | |
.attr({ | |
'class': 'column' | |
}); | |
columns.selectAll('rect.cell') | |
.data(function(d){ return d; }) | |
.enter().append('rect') | |
.classed('cell', true) | |
.classed('out-of-range', function(d){ return d.value === null; }) | |
.attr({ | |
x: function(d, i, pI){ return config.cellWidth * pI; }, | |
y: function(d, i, pI){ return config.cellHeight * i; }, | |
width: config.cellWidth, | |
height: config.cellHeight, | |
fill: function(d){ return config.colorScale(d.value); } | |
}) | |
.on('mousemove', function(d){ | |
var currentCell = d3.select(this); | |
var currentCellX = currentCell.attr('x'); | |
var currentCellY = currentCell.attr('y'); | |
tooltip.html(config.dateFormatTooltip(d.date) + '<br>Value: ' + d.value) | |
.style({ | |
display: 'block', | |
left: function(){ | |
return d3.event.clientX - this.offsetWidth + 'px'; | |
}, | |
top: function(){ | |
return d3.event.clientY - this.offsetHeight + 'px'; | |
} | |
}); | |
cellHover.attr({ | |
x: currentCellX, | |
y: currentCellY, | |
fill: config.colorScale(d.value) | |
}) | |
.style({display: 'block'}) | |
}) | |
.on('mouseout', function(){ | |
tooltip.style({ display: 'none'}); | |
cellHover.style({display: 'none'}) | |
}); | |
columns.exit().remove(); | |
chartContainer.selectAll('text.label.x') | |
.data(d3.transpose(data)[0]) | |
.enter().append('text') | |
.attr({ | |
'class': 'label x', | |
x: function(d, i){ return config.cellWidth * i; }, | |
dy: function(d, i){ return - config.labelAxisPadding; }, | |
transform: function(d, i){ | |
return 'rotate(-60, ' + (config.cellWidth * i + 5) + ', -10)'; | |
} | |
}) | |
.text(function(d){ return config.dateFormatX(new Date(d.date)); }); | |
chartContainer.selectAll('text.label.y') | |
.data(data[0]) | |
.enter().append('text') | |
.attr({ | |
'class': 'label y', | |
y: function(d, i){ return config.cellHeight * i + config.cellHeight / 2; } | |
}) | |
.text(function(d){ return config.dateFormatY(new Date(d.date)); }) | |
.attr({ | |
dx: function(d, i){ return -this.getBBox().width - config.labelAxisPadding; } | |
}); | |
var cellHover = chartContainer.append('rect') | |
.style({ display: 'none'}) | |
.attr({ | |
'class': 'cell-hover', | |
width: config.cellWidth, | |
height: config.cellHeight | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment