An implementation of Tufte's sparklines, based off of http://www.tnoda.com/blog/2013-12-19
Added data generator, transitions, and click event
An implementation of Tufte's sparklines, based off of http://www.tnoda.com/blog/2013-12-19
Added data generator, transitions, and click event
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>D3 Sparklines</title> | |
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script> | |
<link rel="stylesheet" type="text/css" href="sparklines.css"/> | |
</head> | |
<body> | |
<div id="sparkline-title">Basic Sparklines</div> | |
<div id="graph-container"> | |
<div class="label-wrapper"> | |
<div id="cost-label">Estimated Cost</div> | |
<div id="count-label">Count</div> | |
<div id="rating-label">Rating</div> | |
</div> | |
<div class="col-wrapper" id="a-info-wrapper"> | |
<div class="title">Group A</div> | |
<div class="sparkline-wrapper" id="sparkline-a-cost"></div> | |
<div class="sparkline-wrapper" id="sparkline-a-meetings"></div> | |
<div class="sparkline-wrapper" id="sparkline-a-avgRating"></div> | |
</div> | |
<div class="col-wrapper" id="b-info-wrapper"> | |
<div class="title">Group B</div> | |
<div class="sparkline-wrapper" id="sparkline-b-cost"></div> | |
<div class="sparkline-wrapper" id="sparkline-b-meetings"></div> | |
<div class="sparkline-wrapper" id="sparkline-b-avgRating"></div> | |
</div> | |
<div class="col-wrapper" id="c-info-wrapper"> | |
<div class="title">Group C</div> | |
<div class="sparkline-wrapper" id="sparkline-c-cost"></div> | |
<div class="sparkline-wrapper" id="sparkline-c-meetings"></div> | |
<div class="sparkline-wrapper" id="sparkline-c-avgRating"></div> | |
</div> | |
</div> | |
</body> | |
<script type="text/javascript" src="sparklines.js"></script> | |
</html> |
body{ | |
font-family: Helvetica; | |
} | |
#graph-container{ | |
position: relative; | |
display: inline-block; | |
width: 90%; | |
left: 5%; | |
height:175px; | |
} | |
#sparkline-title{ | |
display: block; | |
position: relative; | |
width: 70%; | |
margin-left: 15%; | |
text-align: center; | |
border-bottom: thin solid black; | |
margin-top: 10px; | |
margin-bottom: 30px; | |
padding-bottom: 10px; | |
font-size: 20px; | |
} | |
.label-wrapper{ | |
position: absolute; | |
width: 10%; | |
display: inline-block; | |
text-align: right; | |
top:35px; | |
} | |
#count-label, #rating-label{ | |
margin-top: 40px; | |
} | |
.title{ | |
margin-bottom: 10px; | |
} | |
#a-info-wrapper{ | |
margin-left: 10%; | |
} | |
.col-wrapper{ | |
position: relative; | |
display: inline-block; | |
width: 25%; | |
text-align: center; | |
} | |
.sparkline { | |
fill: none; | |
stroke: #000; | |
stroke-width: 0.5px; | |
} | |
.sparkcircle { | |
fill: red; | |
stroke: none; | |
} | |
.sparkline-wrapper{ | |
position: relative; | |
display: inline-block; | |
height: 40px; | |
cursor:pointer; | |
width:70%; | |
color: black; | |
margin-bottom: 15px; | |
} | |
#insight-row-avgRating{ | |
top:-12px; | |
} | |
.text-value{ | |
position: absolute; | |
white-space: nowrap; | |
overflow: hidden; | |
} | |
.text-flip-number{ | |
position: absolute; | |
color:steelblue; | |
font-size: 18px; | |
text-align: center; | |
left: 105%; | |
top:-2px; | |
} | |
.flip-descriptor-tag{ | |
position: absolute; | |
font-size: 16px; | |
text-align: center; | |
left:10%; | |
top:8px; | |
} |
var animDuration = 1000; | |
//http://stackoverflow.com/questions/1527803/generating-random-numbers-in-javascript-in-a-specific-range | |
function getRandomInt(min, max) { | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
function generateExecSparklineData(min,max){ | |
var returnArray=[]; | |
var datePointer = new Date(); | |
var i=0; | |
//off by 1 error here, not sure why | |
datePointer = new Date(datePointer.setDate(datePointer.getDate()+1)); | |
while(i<30){ | |
returnArray.push({ | |
date:datePointer, | |
value:getRandomInt(min,max) | |
}); | |
datePointer = new Date(datePointer.setDate(datePointer.getDate()-1)); | |
i++; | |
} | |
return returnArray; | |
} | |
//http://www.tnoda.com/blog/2013-12-19 | |
function sparkline(elemId, data) { | |
function numberWithCommas(x) { | |
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); | |
} | |
function prettyPrint(id,value){ | |
value = numberWithCommas(value); | |
if (id.indexOf("cost") > -1){ | |
return "$"+value; | |
} | |
else{ | |
return value; | |
} | |
} | |
data.forEach(function(d){ | |
d.date = new Date(d.date); | |
}) | |
function getSummation(){ | |
var sum = 0; | |
for(var i=0; i<data.length; i++){ | |
sum = sum+data[i].value; | |
} | |
return sum; | |
} | |
function clickEventHandler(){ | |
var element = document.getElementById(elemId.replace("#","")); | |
var isFlipped = element.getAttribute("data-flipped"); | |
console.log(element); | |
if(!(elemId.indexOf("avgRating") > -1)){ | |
if(isFlipped === "false"){ | |
d3.select("#"+"current-path-"+elemId.replace("#","")).transition().duration(animDuration/1.5).style("opacity",0) | |
d3.select("#this-spark-circle-"+elemId.replace("#","")).transition().duration(animDuration/1.5).style("opacity",0); | |
d3.select("#text-value-"+elemId.replace("#","")).transition().duration(animDuration/1.5).style("opacity",0); | |
d3.select(elemId+"-flip-number").transition().delay(animDuration/2).duration(animDuration/2).style("opacity",1); | |
d3.select(elemId+"-flip-number-label").transition().delay(animDuration/2).duration(animDuration/2).style("opacity",1); | |
d3.select(elemId).attr("data-flipped",true); | |
} | |
else{ | |
d3.select("#"+"current-path-"+elemId.replace("#","")).transition().delay(animDuration/1.5).duration(animDuration/2).style("opacity",1) | |
d3.select("#this-spark-circle-"+elemId.replace("#","")).transition().delay(animDuration/1.5).duration(animDuration/2).style("opacity",1); | |
d3.select("#text-value-"+elemId.replace("#","")).transition().delay(animDuration/1.5).duration(animDuration/2).style("opacity",1); | |
d3.select(elemId+"-flip-number").transition().duration(animDuration/1.5).style("opacity",0); | |
d3.select(elemId+"-flip-number-label").transition().duration(animDuration/1.5).style("opacity",0); | |
d3.select(elemId).attr("data-flipped",false); | |
} | |
} | |
} | |
//leave room for the text value to the right | |
var width = $(elemId).width()-20; | |
var height = $(elemId).height(); | |
var x = d3.scale.linear().range([width-10, 0]); | |
var y = d3.scale.linear().range([height-10, 0]); | |
x.domain(d3.extent(data, function(d) { return d.date; })); | |
y.domain(d3.extent(data, function(d) { return d.value; })); | |
var line = d3.svg.line() | |
.interpolate("basis") | |
.x(function(d) { return x(d.date); }) | |
.y(function(d) { return y(d.value); }); | |
var svg = d3.select(elemId) | |
.append('svg') | |
.attr('width', width) | |
.attr("class","sparkline-wrapper-svg") | |
.attr('height', height) | |
.append('g') | |
.attr('transform', 'translate(0, 2)'); | |
svg.append('path') | |
.datum(data) | |
.attr('class', 'sparkline') | |
.attr("id","current-path-"+elemId.replace("#","")) | |
.attr('d', line); | |
svg.append('circle') | |
.attr('class', 'sparkcircle') | |
.attr("id","this-spark-circle-"+elemId.replace("#","")) | |
.attr('cx', x(data[data.length-1].date)) | |
.attr('cy', y(data[data.length-1].value)) | |
.style("opacity",0) | |
.attr('r', 2); | |
var totalLength = d3.select("#current-path-"+elemId.replace("#","")).node().getTotalLength(); | |
d3.select("#current-path-"+elemId.replace("#","")) | |
.attr("stroke-dasharray", totalLength + " " + totalLength) | |
.attr("stroke-dashoffset", totalLength) | |
.transition() | |
.duration(animDuration) | |
.ease("linear") | |
.attr("stroke-dashoffset", 0); | |
d3.select(elemId).append("text") | |
.attr("class","text-value") | |
.style("opacity","0") | |
.attr("id","text-value-"+elemId.replace("#","")) | |
.text(prettyPrint(elemId,data[data.length-1].value)); | |
d3.select("#this-spark-circle-"+elemId.replace("#","")).transition().delay(animDuration).duration(animDuration/1.5).style("opacity",1); | |
d3.select("#text-value-"+elemId.replace("#","")).transition().delay(animDuration).duration(animDuration/1.5).style("opacity",1); | |
d3.select(elemId).append("text") | |
.attr("class","flip-descriptor-tag") | |
.attr("id",elemId.replace("#","")+"-flip-number-label") | |
.style("opacity","0") | |
.text("Total:") | |
.append("text") | |
.attr("class","text-flip-number") | |
.attr("id",elemId.replace("#","")+"-flip-number") | |
.style("opacity","0") | |
.text(prettyPrint(elemId,getSummation())); | |
d3.select(elemId) | |
.attr("data-flipped",false) | |
.on("click",clickEventHandler); | |
} | |
$( document ).ready(function() { | |
sparkline('#sparkline-a-cost', generateExecSparklineData(1000,2000)); | |
sparkline('#sparkline-a-meetings', generateExecSparklineData(10,60)); | |
sparkline('#sparkline-a-avgRating', generateExecSparklineData(1,4)); | |
sparkline('#sparkline-b-cost', generateExecSparklineData(1000,2000)); | |
sparkline('#sparkline-b-meetings', generateExecSparklineData(10,60)); | |
sparkline('#sparkline-b-avgRating', generateExecSparklineData(1,4)); | |
sparkline('#sparkline-c-cost', generateExecSparklineData(10000,20000)); | |
sparkline('#sparkline-c-meetings', generateExecSparklineData(100,600)); | |
sparkline('#sparkline-c-avgRating', generateExecSparklineData(1,3)); | |
}); |