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.
xxxxxxxxxx
<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="https://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>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js