Last active
December 17, 2015 02:28
-
-
Save imjasonh/5535583 to your computer and use it in GitHub Desktop.
WIP d3.js-based version of Flywheel stats visualization
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<html> | |
<head> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
padding: 0; | |
margin: 0; | |
} | |
svg { | |
display: block; | |
border: 1px solid #999; | |
border-radius: 10px; | |
} | |
svg.allClasses { | |
margin: auto; | |
margin-top: 20px; | |
} | |
div.tooltip { | |
position: absolute; | |
background-color: white; | |
border: 1px solid black; | |
pointer-events: none; | |
border-radius: 5px; | |
padding: 3px; | |
box-shadow: 4px 4px 3px #999; | |
} | |
</style> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
</head> | |
<body> | |
<script type="text/javascript"> | |
var docElem = document.documentElement, | |
body = document.getElementsByTagName('body')[0], | |
x = window.innerWidth || docElem.clientWidth || body.clientWidth, | |
y = window.innerHeight|| docElem.clientHeight|| body.clientHeight; | |
var WIDTH = x * .9 | |
var HEIGHT = y * .9; | |
var MAX_RAD = 40; | |
var MOUSEOVER_GROW_RATIO = 21/20; | |
// Calculate min/max dot center coords to ensure that dots are visible | |
var MIN_X = MAX_RAD * MOUSEOVER_GROW_RATIO; | |
var MAX_X = WIDTH - (MAX_RAD * MOUSEOVER_GROW_RATIO); | |
var MIN_Y = MAX_RAD * MOUSEOVER_GROW_RATIO; | |
var MAX_Y = HEIGHT - (MAX_RAD * MOUSEOVER_GROW_RATIO); | |
var color = d3.scale.category20(); | |
var dataset = [ | |
{"date":"4/3/2013", "instructor": "Grant", "avgRpm": 64, "maxRpm": 98, "avgTorq": 27, "maxTorq": 39, "avgSpeed": 30, "classTime": 45, "totalDistance": 21.3, "total": 291, "minCalories": 798, "maxCalories": 873}, | |
{"date":"4/5/2013", "instructor": "Grant", "avgRpm": 69, "maxRpm": 100, "avgTorq": 28, "maxTorq": 41, "avgSpeed": 32, "classTime": 45, "totalDistance": 23.2, "total": 332, "minCalories": 921, "maxCalories": 996}, | |
{"date":"4/8/2013", "instructor": "Ruth", "avgRpm": 66, "maxRpm": 104, "avgTorq": 27, "maxTorq": 45, "avgSpeed": 31, "classTime": 45, "totalDistance": 22.3, "total": 302, "minCalories": 831, "maxCalories": 906}, | |
{"date":"4/10/2013", "instructor": "Grant", "avgRpm": 67, "maxRpm": 98, "avgTorq": 26, "maxTorq": 40, "avgSpeed": 31, "classTime": 45, "totalDistance": 22.4, "total": 296, "minCalories": 813, "maxCalories": 888}, | |
{"date":"4/17/2013", "instructor": "Grant", "avgRpm": 61, "maxRpm": 96, "avgTorq": 26, "maxTorq": 36, "avgSpeed": 29, "classTime": 45, "totalDistance": 20.5, "total": 268, "minCalories": 729, "maxCalories": 804}, | |
{"date":"4/19/2013", "instructor": "Grant", "avgRpm": 64, "maxRpm": 100, "avgTorq": 26, "maxTorq": 41, "avgSpeed": 30, "classTime": 45, "totalDistance": 21.4, "total": 283, "minCalories": 764, "maxCalories": 849}, | |
{"date":"4/22/2013", "instructor": "Alison", "avgRpm": 69, "maxRpm": 102, "avgTorq": 27, "maxTorq": 39, "avgSpeed": 32, "classTime": 45, "totalDistance": 23.2, "total": 311, "minCalories": 858, "maxCalories": 933}, | |
{"date":"4/24/2013", "instructor": "Grant", "avgRpm": 59, "maxRpm": 96, "avgTorq": 25, "maxTorq": 40, "avgSpeed": 28, "classTime": 45, "totalDistance": 19.8, "total": 255, "minCalories": 690, "maxCalories": 765}, | |
{"date":"5/1/2013", "instructor": "Grant", "avgRpm": 66, "maxRpm": 92, "avgTorq": 26, "maxTorq": 38, "avgSpeed": 31, "classTime": 45, "totalDistance": 22.2, "total": 294, "minCalories": 807, "maxCalories": 882}, | |
{"date":"5/2/2013", "instructor": "Jaimie", "avgRpm": 70, "maxRpm": 102, "avgTorq": 27, "maxTorq": 45, "avgSpeed": 33, "classTime": 45, "totalDistance": 23.3, "total": 313, "minCalories": 864, "maxCalories": 939}, | |
{"date":"5/6/2013", "instructor": "Ruth", "avgRpm": 69, "maxRpm": 102, "avgTorq": 26, "maxTorq": 42, "avgSpeed": 32, "classTime": 45, "totalDistance": 23.1, "total": 300, "minCalories": 825, "maxCalories": 900} | |
]; | |
var access = function(k) { | |
return function(d) { | |
return d[k]; | |
}; | |
}; | |
var minRpm = d3.min(dataset, access("avgRpm")); | |
var maxRpm = d3.max(dataset, access("avgRpm")); | |
var rpmDiff = maxRpm - minRpm; | |
var minTorq = d3.min(dataset, access("avgTorq")); | |
var maxTorq = d3.max(dataset, access("avgTorq")); | |
var torqDiff = maxTorq - minTorq; | |
var minTotal = d3.min(dataset, access("total")); | |
var maxTotal = d3.max(dataset, access("total")); | |
var totalDiff = maxTotal - minTotal; | |
var grow = function(className, ratio) { | |
var elems = document.getElementsByClassName(className); | |
for (var i = 0; i < elems.length; i++) { | |
elems[i].r.baseVal.value = elems[i].r.baseVal.value * ratio; | |
} | |
}; | |
// Begin D3 code | |
var svg = d3.select("body") | |
.append("svg") | |
.attr("width", WIDTH) | |
.attr("height", HEIGHT) | |
.attr("class", "allClasses") | |
.append("g") | |
.attr("class", "grid"); | |
var tooltip = d3.select("body").append("div") | |
.attr("class", "tooltip") | |
.style("opacity", 0); | |
svg.selectAll("circle") | |
.data(dataset) | |
.enter() | |
.append("circle") | |
.attr("cx", function(d) { return WIDTH - (MIN_X + (MAX_X-MIN_X) * ((maxRpm - d["avgRpm"]) / rpmDiff)); }) | |
.attr("cy", function(d) { return (MIN_Y + (MAX_Y-MIN_Y) * ((maxTorq - d["avgTorq"]) / torqDiff)); }) | |
.attr("r", function(d) { return MAX_RAD * (d["total"]/maxTotal); }) | |
.attr("class", function(d) { return "instructor-" + d["instructor"]; }) | |
.style("fill", function(d) { return color(d["instructor"]); }) | |
.style("stroke", "#444") | |
.style("opacity", ".9") | |
.on("mouseover", function(d) { | |
tooltip.transition() | |
.duration(200) | |
.style("opacity", 1); | |
tooltip.style("top", d3.event.pageY + "px") | |
.style("left", d3.event.pageX + "px"); | |
tooltip.html("<b>" + d["instructor"] + " - " + d["date"] + "</b><br />" + | |
"<b>Torq:</b> " + d["avgTorq"] + "<br />" + | |
"<b>RPM:</b> " + d["avgRpm"] + "<br />" + | |
"<b>Power</b>: " + d["total"]); | |
grow("instructor-" + d["instructor"], MOUSEOVER_GROW_RATIO); | |
}) | |
.on("mouseout", function(d) { | |
tooltip.transition() | |
.duration(200) | |
.style("opacity", 0); | |
grow("instructor-" + d["instructor"], 1 / MOUSEOVER_GROW_RATIO); | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment