Skip to content

Instantly share code, notes, and snippets.

@tonyhschu
Last active July 23, 2016 12:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tonyhschu/3321e88a8e3d2be8fc43756f90530e92 to your computer and use it in GitHub Desktop.
Save tonyhschu/3321e88a8e3d2be8fc43756f90530e92 to your computer and use it in GitHub Desktop.
Time Map

Inspired by this time map post I wanted to build a block that shows a bit more of the intuition about how time maps work. This is a very simple timemap built with D3 that visualizes the speed and frequency of keystrokes.

Built with blockbuilder.org

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
svg { width:100%; height: 100% }
#inputField {
border: none;
border-bottom: 1px solid #333;
margin: 20px 50px;
font-size: 24px;
width: 89%;
}
text {
font-family: Helvetica;
color: #777;
}
</style>
</head>
<body>
<input type="text" id="inputField" />
<svg width="960" height="450">
<g transform="translate(320, 10)" id="axis">
<g id="xAxis"></g>
<g id="yAxis"></g>
<g transform="translate(180, 370)">
<text text-anchor="middle">Time Since Previous Event (ms)</text>
</g>
<g transform="translate(-50, 180) rotate(-90)">
<text text-anchor="middle">Time Until Next Event (ms)</text>
</g>
</g>
<g transform="translate(320, 10)" id="points">
</g>
</svg>
<script>
var events = []
var width = 320
var timeScale = d3.scale.pow().exponent(0.5)
.domain([0, 2000])
.range([0, width])
.clamp(true)
var scatterPlot = d3.select('#points')
var inputField = d3.select('#inputField')
var addCharacter = function(char) {
var now = new Date().getTime()
var sincePrevious = Infinity
if (events.length > 0) {
sincePrevious = now - events[events.length - 1].time
events[events.length - 1].tilNext = sincePrevious
}
events.push({
time: now,
sincePrevious: sincePrevious,
tilNext: Infinity,
char: char
})
render()
}
inputField.on('keydown', function() {
var char = String.fromCharCode(d3.event.keyCode)
addCharacter(char)
})
var xAxis = d3.select("#xAxis").selectAll("g")
.data(timeScale.ticks(12))
.enter().append('g')
.each(function(d, i) {
var g = d3.select(this)
var x = timeScale(d)
g.attr('transform', 'translate(' + x + ',' + width + ')')
var tickLength = 5
if (i % 3 === 1) {
var text = g.append('text')
text.text(d)
.attr('fill', '#888')
.attr('font-size', 11)
.attr('font-family', 'Helvetica')
.attr('y', 20)
tickLength = 10
}
var rect = g.append('rect')
rect
.attr('width', 1)
.attr('height', tickLength)
.attr('fill', '#888')
})
var yAxis = d3.select("#yAxis").selectAll("g")
.data(timeScale.ticks(12))
.enter().append('g')
.each(function(d, i) {
var g = d3.select(this)
var y = width - timeScale(d)
g.attr('transform', 'translate(0,' + y + ')')
var tickLength = 5
if (i % 3 === 1) {
var text = g.append('text')
text.text(d)
.attr('font-size', 11)
.attr('font-family', 'Helvetica')
.attr('fill', '#888')
.attr('x', -10)
.attr('text-anchor', 'end')
tickLength = 10
}
var rect = g.append('rect')
rect
.attr('x', -tickLength)
.attr('width', tickLength)
.attr('height', 1)
.attr('fill', '#888')
})
var render = function() {
var points = scatterPlot.selectAll('text')
.data(events, function(d) { return d.time })
points.enter()
.append('text')
.attr('fill', '#f80')
.attr('r', 2)
.text(function(d) {
return d.char
})
points
.attr('opacity', function(d) {
if (d.tilNext === Infinity) {
return 0.1
} else {
return 0.3
}
})
.transition()
.attr('x', function(d) {
return timeScale(d.sincePrevious)
})
.attr('y', function(d) {
return width - timeScale(d.tilNext)
})
points.exit().remove()
}
var demoString = "This is a time map. Type here and see."
var demoDelay = [111,132,121,132,500,80,90,500,100,500,100,131,178,132,500,133,112,131,122,121,500,89,78,80,67,200,67,87,58,59,200,98,78,88,200,45,65,76,89]
var addNextChar = function(i) {
inputField.attr('value', demoString.substr(0,i))
addCharacter(demoString[i].toUpperCase())
var nextFn = function(next) {
return function() {
addNextChar(next)
}
}(i + 1)
if (i < demoString.length) {
setTimeout(nextFn, demoDelay[i])
}
}
setTimeout(function() {
addNextChar(0)
}, 1200)
//inputField.node().focus()
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment