Skip to content

Instantly share code, notes, and snippets.

@imjasonh
Last active December 17, 2015 02:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save imjasonh/5535583 to your computer and use it in GitHub Desktop.
Save imjasonh/5535583 to your computer and use it in GitHub Desktop.
WIP d3.js-based version of Flywheel stats visualization
<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