Skip to content

Instantly share code, notes, and snippets.

@rickdaalhuizen90
Last active December 30, 2021 22:00
Show Gist options
  • Save rickdaalhuizen90/eff438e75691fa91d3fa75a17fcccc8a to your computer and use it in GitHub Desktop.
Save rickdaalhuizen90/eff438e75691fa91d3fa75a17fcccc8a to your computer and use it in GitHub Desktop.
Calendar heatmap
<section>
<h3>Uptime</h3>
<p>Overview of site uptime</p>
<div id="heatmap"></div>
</section>
<script>
import {onMount} from 'svelte';
import matrix from 'calendar-matrix';
const width = 280;// Calendar width
const height = 250;// Calendar height
const padding = 6; // Padding between squares
const square = (width / 7) - padding; // The size of a square
const radius = 5; // Border radius on square
const blend = 65; // Text blend mode
const colorSchema = d3.interpolateBlues;
/**
* Matrix y cordinates (y position)
*
* 0 0 0 0 0
* 25 25 25 25 25 25 25
* 50 50 50 50 50 50 50
* 75 75 75 75 75 75 75
* 100 100
*/
function getYPos(d) {
let date = new Date(d.date);
let ypos = 0;
matrix(date.getFullYear(), date.getMonth()).map((week, i) => {
week.map(val => {
if (val === date.getDate()) {
ypos = i;
}
});
});
return !ypos ? 0 : ypos * (square + padding); // the ypos * square size
}
/**
* Matrix x cordinates (y position)
*
* 125 150
* 0 25 50 75 100 125 150
* 0 25 50 75 100 125 150
* 0 25 50 75 100 125 150
* 0 25
*/
function getXPos(d) {
let xpos = new Date(d.date).getDay() - 1;
return !xpos ? 0 : xpos * (square + padding); // the xpos * square size
}
function Draw(month, data) {
const months = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
// Square color
const color = d3.scaleSequential()
.interpolator(colorSchema)
.domain([100, 0]);
// Draw element
const svg = d3.select('#heatmap')
.append('svg')
.attr('width', width)
.attr('height', height);
// Title of the month
svg.append('text')
.text(months[month-1])
.attr('x', 0)
.attr('y', height - (padding * 2))
.attr('font-size', '1.5rem')
.attr('font-weight', 'bold')
.attr('fill', '#000');
// All squares in the calendar
const g = svg.append('g').selectAll('g')
.data(data, d => d)
.enter().append('g');
// Size and position the squares
g.append('rect')
.attr('width', square)
.attr('height', square)
.attr('rx', radius)
.attr("x", d => getXPos(d))
.attr("y", d => getYPos(d))
.attr('fill', d => color(d.percentage));
// Text on hover (Todo: create custom tooltip, because of long browser delay)
g.append('title')
.text(d => `${d.percentage}%`);
// Text inside square
g.append('text')
.attr("x", d => getXPos(d) + (square / 2))
.attr("y", d => getYPos(d) + (square / 2))
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.attr('font-size', 12)
.attr('fill', d => d.percentage < blend ? '#fff' : '#000')
.text(d => new Date(d.date).getDate());
}
onMount(() => {
d3.json('./data.json').then(
data => Object.keys(data).map(key => Draw(key, data[key])),
);
});
</script>
<style scoped>
section {
width: 100%;
height: 100%;
}
#heatmap {
display: flex;
justify-content: center;
gap: 1rem;
}
:global(#heatmap .subdomain-text) {
fill: #fff;
opacity: 1;
font-size: 8px;
}
:global(#heatmap svg) {
padding: 3px;
}
</style>
var _ = require('lodash');
var rows = _.range(0, 6);
var cols = _.range(0, 7);
function getMatrix (y, m) {
var matrix = [];
var date = new Date(y, m);
var numDays = new Date(y, m + 1, 0).getDate();
var dayNum;
_.each(rows, function (row) {
var week = [];
_.each(cols, function (col) {
if (row == 0) {
dayNum = col - date.getDay() + 1;
week.push(col < date.getDay() ? -(new Date(y, m, -(date.getDay() - 1 - col)).getDate()) : dayNum);
} else {
dayNum = _.last(matrix)[6] + col + 1;
week.push(dayNum <= numDays ? dayNum : -(dayNum - numDays));
}
});
if (!row || week[0] > 1) matrix.push(week);
});
return matrix;
}
module.exports = function(year, month) {
if (typeof year === 'undefined') year = new Date();
if (year instanceof Date) {
return getMatrix(year.getFullYear(), year.getMonth());
} else {
return getMatrix(year, month);
}
};
{"10":[{"date":"2021-10-31","percentage":77},{"date":"2021-10-30","percentage":69},{"date":"2021-10-29","percentage":18},{"date":"2021-10-28","percentage":60},{"date":"2021-10-27","percentage":25},{"date":"2021-10-26","percentage":24},{"date":"2021-10-25","percentage":20},{"date":"2021-10-24","percentage":22},{"date":"2021-10-23","percentage":72},{"date":"2021-10-22","percentage":24},{"date":"2021-10-21","percentage":67},{"date":"2021-10-20","percentage":45},{"date":"2021-10-19","percentage":47},{"date":"2021-10-18","percentage":66},{"date":"2021-10-17","percentage":48},{"date":"2021-10-16","percentage":58},{"date":"2021-10-15","percentage":1},{"date":"2021-10-14","percentage":39},{"date":"2021-10-13","percentage":82},{"date":"2021-10-12","percentage":30},{"date":"2021-10-11","percentage":31},{"date":"2021-10-10","percentage":36},{"date":"2021-10-09","percentage":18},{"date":"2021-10-08","percentage":80},{"date":"2021-10-07","percentage":78},{"date":"2021-10-06","percentage":28},{"date":"2021-10-05","percentage":34},{"date":"2021-10-04","percentage":98},{"date":"2021-10-03","percentage":9},{"date":"2021-10-02","percentage":53},{"date":"2021-10-01","percentage":32}],"11":[{"date":"2021-11-30","percentage":56},{"date":"2021-11-29","percentage":94},{"date":"2021-11-28","percentage":29},{"date":"2021-11-27","percentage":25},{"date":"2021-11-26","percentage":82},{"date":"2021-11-25","percentage":6},{"date":"2021-11-24","percentage":67},{"date":"2021-11-23","percentage":19},{"date":"2021-11-22","percentage":60},{"date":"2021-11-21","percentage":73},{"date":"2021-11-20","percentage":23},{"date":"2021-11-19","percentage":19},{"date":"2021-11-18","percentage":5},{"date":"2021-11-17","percentage":64},{"date":"2021-11-16","percentage":16},{"date":"2021-11-15","percentage":65},{"date":"2021-11-14","percentage":12},{"date":"2021-11-13","percentage":52},{"date":"2021-11-12","percentage":76},{"date":"2021-11-11","percentage":15},{"date":"2021-11-10","percentage":81},{"date":"2021-11-09","percentage":12},{"date":"2021-11-08","percentage":19},{"date":"2021-11-07","percentage":62},{"date":"2021-11-06","percentage":60},{"date":"2021-11-05","percentage":36},{"date":"2021-11-04","percentage":34},{"date":"2021-11-03","percentage":59},{"date":"2021-11-02","percentage":53},{"date":"2021-11-01","percentage":5}],"12":[{"date":"2021-12-30","percentage":44},{"date":"2021-12-29","percentage":56},{"date":"2021-12-28","percentage":83},{"date":"2021-12-27","percentage":53},{"date":"2021-12-26","percentage":84},{"date":"2021-12-25","percentage":98},{"date":"2021-12-24","percentage":81},{"date":"2021-12-23","percentage":82},{"date":"2021-12-22","percentage":89},{"date":"2021-12-21","percentage":21},{"date":"2021-12-20","percentage":91},{"date":"2021-12-19","percentage":52},{"date":"2021-12-18","percentage":43},{"date":"2021-12-17","percentage":74},{"date":"2021-12-16","percentage":15},{"date":"2021-12-15","percentage":60},{"date":"2021-12-14","percentage":39},{"date":"2021-12-13","percentage":89},{"date":"2021-12-12","percentage":87},{"date":"2021-12-11","percentage":52},{"date":"2021-12-10","percentage":64},{"date":"2021-12-09","percentage":59},{"date":"2021-12-08","percentage":91},{"date":"2021-12-07","percentage":1},{"date":"2021-12-06","percentage":74},{"date":"2021-12-05","percentage":42},{"date":"2021-12-04","percentage":24},{"date":"2021-12-03","percentage":85},{"date":"2021-12-02","percentage":2},{"date":"2021-12-01","percentage":43}]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment