Skip to content

Instantly share code, notes, and snippets.

@apburnes
Last active December 27, 2015 15:39
Show Gist options
  • Save apburnes/7349155 to your computer and use it in GitHub Desktop.
Save apburnes/7349155 to your computer and use it in GitHub Desktop.
Socrata Clock Rotations

Using D3.js to calculate and visualize hour and minute hand rotations between two times. Using some artistic license with the Socrata logo to visualize the analog rotations.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
html {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 16px;
font-weight: bold;
}
form {
position: absolute;
left: 10px;
top: 10px;
}
.minTick {
fill: #EF3D3A;
stroke: #ffffff;
stroke-width: 15px;
stroke-dasharray: 105 , 76;
stroke-linecap: square;
}
.hourTick {
fill: #EF3D3A;
stroke: #ffffff;
stroke-width: 15px;
stroke-dasharray: 313, 200;
stroke-linecap: square;
}
.circle {
fill: none;
stroke-width: 25px;
}
.inner {
stroke: #969696;
}
.center {
stroke: #676767;
}
.outer {
stroke: #343434;
}
.text {
position: absolute;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 24px;
font-weight: bold;
}
</style>
<body>
<input label="Start Time" id="start-time" type="time" value="00:00"></input>
<input label="End Time" id="end-time" type="time" value="00:00"></input>
<button type="submit" id="submit-times">Calc Rotation</button>
<div id="viz"></div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
//Set viz container
var width = 960,
height = 500,
minRot = 0,
hourRot = 0;
var svg = d3.select("#viz").append("svg")
.attr("width", width)
.attr("height", height)
//Defining the logo svg
var innerCircle = svg.append("circle")
.attr("r", 70)
.attr("cx", width/2)
.attr("cy", height/2)
.attr("class", "inner circle")
var centerCircle = svg.append("circle")
.attr("r", 118)
.attr("cx", width/2)
.attr("cy", height/2)
.attr("class", "center circle")
var outerCircle = svg.append("circle")
.attr("r", 165)
.attr("cx", width/2)
.attr("cy", height/2)
.attr("class", "outer circle")
var minTickData = [{"x": (width/2), "y": (height/2) - 40},
{"x": (width/2) + 40, "y": ((height/2) - 135)},
{"x": (width/2) - 40, "y": ((height/2) - 135)},
{"x": (width/2), "y": (height/2) - 40}
];
var hourTickData = [{"x": (width/2) - 39, "y": ((height/2) - 133)},
{"x": (width/2) - 74, "y": ((height/2) - 210)},
{"x": (width/2) + 74, "y": ((height/2) - 210)},
{"x": (width/2) + 39, "y": ((height/2) - 133)},
{"x": (width/2) - 39, "y": ((height/2) - 133)}
];
var tickFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate("linear");
var minTick = svg.append("path")
.attr("transform", "translate(0,0)")
.attr("d", tickFunction(minTickData))
.attr("class", "minTick")
.attr("transform", "rotate(45, 480, 250)");
var hourTick = svg.append("path")
.attr("transform", "translate(0,0)")
.attr("d", tickFunction(hourTickData))
.attr("class", "hourTick")
.attr("transform", "rotate(45, 480, 250)");
var minText = svg.append("text")
.attr("class", "text")
.attr("dy", 40)
var hourText = svg.append("text")
.attr("class", "text")
.attr("dy", 70)
// spinArc Function handles the data parsing and passing it into d3
function spinArc(start, end) {
var startMins = totMins(start);
var endMins = totMins(end);
var setHr = hourStart(start);
var setMins = minStart(start);
if (startMins > endMins) {
endMins = endMins + 1440;
}
var diff = endMins - startMins;
var minSpinDeg = (diff * 6);
var minRotation = ((diff * 6)/360);
var hourSpinDeg = ((diff/60)*30);
var hourRotation = ((diff/60)*30)/360;
// Start and Attribute the Minute Rotation Counter/Transition
minText.transition()
.duration(3000)
.tween('text', function () {
var i = d3.interpolate(3, 0);
return function (t) {
var countDown = i(t);
minText.text("Starting Clock in " + countDown.toFixed(0));
};
})
.each("end", countMinRotation);
function countMinRotation (d) {
d3.select(this).transition()
.delay(1000)
.duration(3000)
.tween('text', function () {
var i = d3.interpolate(minRot, minRotation);
return function (t) {
minRot = i(t);
minText.text("Minute Rotations: " + minRot.toFixed(2));
};
})
.each("end", resetMinText);
}
function resetMinText (d) {
d3.select(this).transition()
.delay(3000)
.duration(800)
.tween('text', function () {
var i = d3.interpolate(minRot, 0);
return function (t) {
minRot = i(t);
minText.text("Minute Rotations: " + minRot.toFixed(2));
};
});
}
// Start and Attribute the Hour Rotation Counter/Transition
hourText.transition()
.duration(3000)
.each("end", countHourRotation);
function countHourRotation (d) {
d3.select(this).transition()
.delay(1000)
.duration(3000)
.tween('text', function () {
var i = d3.interpolate(hourRot, hourRotation);
return function (t) {
hourRot = i(t);
hourText.text("Hour Rotations: " + hourRot.toFixed(2));
};
})
.each("end", resetHourText);
}
function resetHourText (d) {
d3.select(this).transition()
.delay(3000)
.duration(800)
.tween('text', function () {
var i = d3.interpolate(hourRot, 0);
return function (t) {
hourRot = i(t);
hourText.text("Hour Rotations: " + hourRot.toFixed(2));
};
});
}
// Start the rotations of the minute hand
minTick.transition()
.duration(3000)
.attrTween("transform", setMinTween)
.each("end", startMinRot)
function setMinTween(d,i,a) {
return d3.interpolateString(a, "rotate(" + setMins + ". 480, 250)");
}
function startMinRot (d) {
d3.select(this).transition()
.delay(1000)
.duration(3000)
.attrTween("transform", minTween)
.each("end", resetTicks);
}
function minTween(d, i, a) {
var sumSpin = parseInt(minSpinDeg, 10) + parseInt(setMins, 10);
return d3.interpolateString(a, "rotate("+ sumSpin +", 480, 250)");
}
// Start the rotations of the Hour Hand
hourTick.transition()
.duration(3000)
.attrTween("transform", setHourTween)
.each("end", startHourRot)
function setHourTween(d,i,a) {
return d3.interpolateString(a, "rotate(" + setHr + ". 480, 250)");
}
function startHourRot (d) {
d3.select(this).transition()
.delay(1000)
.duration(3000)
.attrTween("transform", hourTween)
.each("end", resetTicks);
}
function hourTween(d, i, a) {
var sumSpin = parseInt(hourSpinDeg, 10) + parseInt(setHr, 10);
return d3.interpolate(a, "rotate("+ sumSpin +", 480, 250)");
}
// Reset the Minute and Hour hand to the original logo
function resetTicks (d) {
d3.select(this).transition()
.delay(3000)
.duration(800)
.attrTween("transform", resetTween);
}
function resetTween(d, i, a) {
return d3.interpolate(a, String("rotate(45, 480, 250)"));
}
};
//Parse input time data
function totMins(time) {
var totMins = parseInt(time.split(':')[0]*60, 10) + parseInt(time.split(':')[1], 10);
return totMins;
}
function hourStart(time1) {
var startHr = (parseInt(time1.split(':')[0], 10)*30) + (parseInt(time1.split(':')[1], 10) / 2);
return startHr.toFixed(0);
}
function minStart(time1) {
var startMins = (parseInt(time1.split(':')[1]) * 6);
return startMins;
}
//On "Calc Rotation" button click, Initiate the rotation sequence.
d3.select('#submit-times')
.on("click", function () {
var startTime = document.getElementById("start-time").value;
var endTime = document.getElementById("end-time").value;
spinArc(startTime, endTime);
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment