Skip to content

Instantly share code, notes, and snippets.

@kaezarrex
Last active September 22, 2021 08:40
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kaezarrex/10122633 to your computer and use it in GitHub Desktop.
Save kaezarrex/10122633 to your computer and use it in GitHub Desktop.
punchcard-js
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<meta charset="utf-8">
<title>punchcard-js</title>
<style>
@import url("style.css");
</style>
</head>
<body>
<div id="chart"></div>
<script src="main.js"></script>
<button onclick="load('sample1.csv')">data1</button>
<button onclick="load('sample2.csv')">data2</button>
<button onclick="load('sample3.csv')">data3</button>
<button onclick="load('sample4.csv')">data4</button>
<button onclick="load('sample5.csv')">battleship</button>
</body>
</html>
MIT License
Copyright (c) 2019 David Hazinski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
var margin = {top: 10, right: 10, bottom: 10, left: 15}
var width = 960 - margin.left - margin.right
var height = 405 - margin.top - margin.bottom
var padding = 3
var xLabelHeight = 30
var yLabelWidth = 80
var borderWidth = 3
var duration = 500
var chart = d3.select('#chart').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 + ')')
var border = chart.append('rect')
.attr('x', yLabelWidth)
.attr('y', xLabelHeight)
.style('fill-opacity', 0)
.style('stroke', '#000')
.style('stroke-width', borderWidth)
.style('shape-rendering', 'crispEdges')
load('sample1.csv')
function load(name) {
d3.text(name, function(dataCSV) {
var labelsX = null
var data = []
d3.csv.parseRows(dataCSV, function(d) {
if (labelsX === null) return labelsX = d.slice(1)
var values = d.slice(1)
var i = 0
for (; i < values.length; i++) {
values[i] = parseInt(values[i], 10)
}
data.push({
label: d[0],
values: values
})
})
update(data, labelsX)
})
}
function update(data, labelsX) {
var allValues = Array.prototype.concat.apply([], data.map(function(d) { return d.values }))
var maxWidth = d3.max(data.map(function(d) { return d.values.length }))
var maxR = d3.min([(width - yLabelWidth) / maxWidth, (height - xLabelHeight) / data.length]) / 2
var r = function(d) {
if (d === 0) return 0
f = d3.scale.sqrt()
.domain([d3.min(allValues), d3.max(allValues)])
.rangeRound([2, maxR - padding])
return f(d)
}
var c = d3.scale.linear()
.domain([d3.min(allValues), d3.max(allValues)])
.rangeRound([255 * 0.8, 0])
var rows = chart.selectAll('.row')
.data(data, function(d){ return d.label })
rows.enter().append('g')
.attr('class', 'row')
rows.exit()
.transition()
.duration(duration)
.style('fill-opacity', 0)
.remove()
rows.transition()
.duration(duration)
.attr('transform', function(d, i){ return 'translate(' + yLabelWidth + ',' + (maxR * i * 2 + maxR + xLabelHeight) + ')' })
var dots = rows.selectAll('circle')
.data(function(d){ return d.values })
dots.enter().append('circle')
.attr('cy', 0)
.attr('r', 0)
.style('fill', '#ffffff')
.text(function(d){ return d })
dots.exit()
.transition()
.duration(duration)
.attr('r', 0)
.remove()
dots.transition()
.duration(duration)
.attr('r', function(d){ return r(d) })
.attr('cx', function(d, i){ return i * maxR * 2 + maxR })
.style('fill', function(d){ return 'rgb(' + c(d) + ',' + c(d) + ',' + c(d) + ')' })
var dotLabels = rows.selectAll('.dot-label')
.data(function(d){ return d.values })
var dotLabelEnter = dotLabels.enter().append('g')
.attr('class', 'dot-label')
.on('mouseover', function(d){
var selection = d3.select(this)
selection.select('rect').transition().duration(100).style('opacity', 1)
selection.select("text").transition().duration(100).style('opacity', 1)
})
.on('mouseout', function(d){
var selection = d3.select(this)
selection.select('rect').transition().style('opacity', 0)
selection.select("text").transition().style('opacity', 0)
})
dotLabelEnter.append('rect')
.style('fill', '#000000')
.style('opacity', 0)
dotLabelEnter.append('text')
.style('text-anchor', 'middle')
.style('fill', '#ffffff')
.style('opacity', 0)
dotLabels.exit().remove()
dotLabels
.attr('transform', function(d, i){ return 'translate(' + (i * maxR * 2) + ',' + (-maxR) + ')' })
.select('text')
.text(function(d){ return d })
.attr('y', maxR + 4)
.attr('x', maxR)
dotLabels
.select('rect')
.attr('width', maxR * 2)
.attr('height', maxR * 2)
var xLabels = chart.selectAll('.xLabel')
.data(labelsX)
xLabels.enter().append('text')
.attr('y', xLabelHeight)
.attr('transform', 'translate(0,-6)')
.attr('class', 'xLabel')
.style('text-anchor', 'middle')
.style('fill-opacity', 0)
xLabels.exit()
.transition()
.duration(duration)
.style('fill-opacity', 0)
.remove()
xLabels.transition()
.text(function (d) { return d })
.duration(duration)
.attr('x', function(d, i){ return maxR * i * 2 + maxR + yLabelWidth })
.style('fill-opacity', 1)
var yLabels = chart.selectAll('.yLabel')
.data(data, function(d){ return d.label })
yLabels.enter().append('text')
.text(function (d) { return d.label })
.attr('x', yLabelWidth)
.attr('class', 'yLabel')
.style('text-anchor', 'end')
.style('fill-opacity', 0)
yLabels.exit()
.transition()
.duration(duration)
.style('fill-opacity', 0)
.remove()
yLabels.transition()
.duration(duration)
.attr('y', function(d, i){ return maxR * i * 2 + maxR + xLabelHeight })
.attr('transform', 'translate(-6,' + maxR / 2.5 + ')')
.style('fill-opacity', 1)
var vert = chart.selectAll('.vert')
.data(labelsX)
vert.enter().append('line')
.attr('class', 'vert')
.attr('y1', xLabelHeight + borderWidth / 2)
.attr('stroke', '#888')
.attr('stroke-width', 1)
.style('shape-rendering', 'crispEdges')
.style('stroke-opacity', 0)
vert.exit()
.transition()
.duration(duration)
.style('stroke-opacity', 0)
.remove()
vert.transition()
.duration(duration)
.attr('x1', function(d, i){ return maxR * i * 2 + yLabelWidth })
.attr('x2', function(d, i){ return maxR * i * 2 + yLabelWidth })
.attr('y2', maxR * 2 * data.length + xLabelHeight - borderWidth / 2)
.style('stroke-opacity', function(d, i){ return i ? 1 : 0 })
var horiz = chart.selectAll('.horiz').
data(data, function(d){ return d.label })
horiz.enter().append('line')
.attr('class', 'horiz')
.attr('x1', yLabelWidth + borderWidth / 2)
.attr('stroke', '#888')
.attr('stroke-width', 1)
.style('shape-rendering', 'crispEdges')
.style('stroke-opacity', 0)
horiz.exit()
.transition()
.duration(duration)
.style('stroke-opacity', 0)
.remove()
horiz.transition()
.duration(duration)
.attr('x2', maxR * 2 * labelsX.length + yLabelWidth - borderWidth / 2)
.attr('y1', function(d, i){ return i * maxR * 2 + xLabelHeight })
.attr('y2', function(d, i){ return i * maxR * 2 + xLabelHeight })
.style('stroke-opacity', function(d, i){ return i ? 1 : 0 })
border.transition()
.duration(duration)
.attr('width', maxR * 2 * labelsX.length)
.attr('height', maxR * 2 * data.length)
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Sunday 0 0 0 0 0 0 3 445 544 818 756 477 538 493 589 611 351 650 211 5 1 0 0 0
Monday 0 0 0 1 0 0 144 2193 2667 5443 5444 5029 6198 4324 4849 4051 2894 2667 1471 832 510 417 64 0
Tuesday 3 5 3 1 0 0 230 1716 2936 3954 4516 3955 4081 3628 3928 3481 3094 2688 2068 1260 1119 622 209 14
Wednesday 0 0 0 9 0 0 242 2308 4310 4680 4065 4727 4615 4628 4964 4282 4748 4564 3215 1642 987 714 306 0
Thursday 0 0 0 3 0 0 247 1992 3912 4536 3436 4633 4083 3728 3516 2339 2915 2345 1403 826 741 375 219 1
Friday 0 0 0 0 0 0 132 1367 2226 2618 1883 2428 2005 1991 2190 1495 1824 1448 800 556 366 319 13 0
Saturday 0 0 0 6 0 0 46 411 624 684 800 332 154 72 98 448 353 532 270 4 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Sunday 0 0 0 0 0 0 132 1367 2226 2618 1883 2428 2005 1991 2190 1495 1824 1448 800 556 366 319 13 0
Monday 0 0 0 6 0 0 46 411 624 684 800 332 154 72 98 448 353 532 270 4 0 0 0 0
Tuesday 0 0 0 0 0 0 3 445 544 818 756 477 538 493 589 611 351 650 211 5 1 0 0 0
Wednesday 0 0 0 1 0 0 144 2193 2667 5443 5444 5029 6198 4324 4849 4051 2894 2667 1471 832 510 417 64 0
Thursday 3 5 3 1 0 0 230 1716 2936 3954 4516 3955 4081 3628 3928 3481 3094 2688 2068 1260 1119 622 209 14
Friday 0 0 0 9 0 0 242 2308 4310 4680 4065 4727 4615 4628 4964 4282 4748 4564 3215 1642 987 714 306 0
Saturday 0 0 0 3 0 0 247 1992 3912 4536 3436 4633 4083 3728 3516 2339 2915 2345 1403 826 741 375 219 1
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Monday 144 2193 2667 5443 5444 5029 6198 4324 4849 4051 2894 2667 1471 832 510 417 64 0
Tuesday 230 1716 2936 3954 4516 3955 4081 3628 3928 3481 3094 2688 2068 1260 1119 622 209 14
Wednesday 242 2308 4310 4680 4065 4727 4615 4628 4964 4282 4748 4564 3215 1642 987 714 306 0
Thursday 247 1992 3912 4536 3436 4633 4083 3728 3516 2339 2915 2345 1403 826 741 375 219 1
Friday 132 1367 2226 2618 1883 2428 2005 1991 2190 1495 1824 1448 800 556 366 319 13 0
0 1 2 3 4
zero 0 1 2 3 4
one 5 6 7 8 9
two 10 11 12 13 14
three 15 16 17 18 19
four 20 21 22 23 24
1 2 3 4 5 6 7 8 9 10
A 1 1 0 1 0 0 1 1 1 1
B 0 0 0 1 0 0 0 0 0 0
C 1 0 0 1 0 1 1 1 0 1
D 1 0 0 0 0 0 0 0 0 1
E 1 0 0 0 0 0 0 0 0 0
F 1 0 0 0 0 1 0 0 0 1
G 0 0 0 0 0 1 0 0 0 1
H 1 1 0 0 0 0 0 0 0 1
I 0 0 0 0 0 0 0 0 0 0
J 0 0 0 0 1 1 1 1 1 1
body {
background-color: #fff;
}
text {
fill: black;
font: 16px sans-serif;
cursor: default;
}
.dot-label text {
font-size: 12px;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment