Last active
June 11, 2016 22:42
-
-
Save olimay/41d1fcacfd208263fa09b8d7db3273d0 to your computer and use it in GitHub Desktop.
Bar Chart Example
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
license: mpl-2.0 |
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
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>D3: A simple bar chart</title> | |
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script> | |
<style type="text/css"> | |
rect:hover { | |
fill: orange; | |
} | |
#tooltip { | |
position: absolute; | |
width: 200px; | |
height: auto; | |
padding: 10px; | |
background-color: white; | |
-webkit-border-radius: 10px; | |
-moz-border-radius: 10px; | |
border-radius: 10px; | |
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); | |
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); | |
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4); | |
pointer-events: none; | |
} | |
#tooltip.hidden { | |
display: none; | |
} | |
#tooltip p { | |
margin: 0; | |
font-family: sans-serif; | |
font-size: 16px; | |
line-height: 20px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="graph"> | |
<div id="tooltip" class="hidden"> | |
<p><strong>Important Label Heading</strong></p> | |
<p><span id="value">100</span>%</p> | |
</div> | |
</div> | |
<div id="controls"></div> | |
<script type="text/javascript"> | |
// Margins, width, height | |
var margin = { top: 0, bottom: 0, left: 0, right: 0 }; | |
var w = 650 - margin.left - margin.right; | |
var h = 250 - margin.top - margin.bottom; | |
var barPadding = 1; | |
var dynamicData = function dynamicData (n, max) { | |
var data = []; | |
for (var i = 0; i < n; i++) { | |
data.push(Math.round(Math.random() * max)); | |
} | |
return data; | |
} | |
var maxvalue = 100; | |
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13, | |
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ]; | |
// Transition defaults | |
var tinyDuration = 100; | |
var tinyDelay = 10; | |
var smallDuration = 500; | |
var smallDelay = 50; | |
var medDuration = 1000; | |
var medDelay = 100; | |
var longDuration = 2000; | |
var longDelay = 200; | |
var defaultDuration = tinyDuration; | |
var defaultDelay = tinyDelay; | |
//Create SVG element | |
var svg; | |
var createSVG = function () { | |
svg = d3.select('.graph') | |
.append('svg') | |
.attr('width', w + margin.left + margin.right) | |
.attr('height', h + margin.top + margin.bottom) | |
.append('g') | |
.attr('class', 'graph') | |
.attr('transform', | |
'translate(' + margin.left + ',' + margin.top + ')'); | |
}; | |
// Scales | |
var xscale = d3.scale.ordinal(); | |
var yscale = d3.scale.linear() | |
var barColorScale = d3.scale.linear() | |
var updateXScale = function updateXScale() { | |
xscale.domain(d3.range(dataset.length)) | |
.rangeRoundBands([0, w], 0.05); | |
}; | |
var updateYScale = function updateYScale() { | |
yscale.domain([0, d3.max(dataset)]) | |
.rangeRound([0, h]); | |
yscale.clamp(); | |
}; | |
var updateColorScale = function updateColorScale() { | |
barColorScale.domain([0,d3.max(dataset)]) | |
.rangeRound([0, 255]) | |
.clamp(); | |
}; | |
var updateScales = function () { | |
updateXScale(); | |
updateYScale(); | |
updateColorScale(); | |
} | |
// Bars | |
function barColor(d) { | |
return 'rgb(0,0,' + barColorScale(d) + ')'; | |
} | |
/** Update */ | |
var updateBars = function (animate, duration, delay) { | |
if (!animate) { | |
duration = 0; | |
delay = 0; | |
} else { | |
if (null == duration) { | |
duration = defaultDuration; | |
} | |
if (null == delay) { | |
delay = defaultDelay; | |
} | |
} | |
svg.selectAll('rect') | |
.data(dataset) | |
.enter() | |
.append('rect') | |
.attr('class', 'bar') | |
.attr('fill', function (d) { | |
return barColor(d); | |
}) | |
.attr('width', w / dataset.length - barPadding) | |
.attr('height', function (d) { | |
return yscale(d); | |
}) | |
.attr('y', function (d) { | |
return h - yscale(d); | |
}) | |
/* horizontal animation */ | |
.attr('x', xscale(0)) | |
.transition() | |
.delay(function (d, i) { | |
console.log(delay); | |
return i * delay; | |
}) | |
.duration(duration) | |
.attr('x', function (d, i) { | |
return xscale(i); | |
}); | |
}; // Update bars | |
// Labels | |
var updateLabels = function (animate, duration, delay) { | |
console.log('Update labels'); | |
if (!animate) { | |
duration = 0; | |
delay = 0; | |
} | |
svg.selectAll('text') | |
.data(dataset) | |
.enter() | |
.append('text') | |
.attr('class', 'bar-label') | |
.text(function (d) { | |
return d; | |
}) | |
.attr('text-anchor', 'middle') | |
.attr('x', function (d, i) { | |
return xscale(i) + xscale.rangeBand() / 2; | |
}) | |
.attr('y', h + 14 ) | |
.transition() | |
.delay(function (d, i) { | |
return i * delay; | |
}) | |
.duration(duration) | |
.attr('y', function (d) { | |
return h - yscale(d) + 14; | |
}) | |
.attr('font-family', 'sans-serif') | |
.attr('font-size', '11px') | |
.attr('fill', 'white'); | |
}; | |
// Animations | |
var easetype = 'cubic-in-out'; | |
var alleasetypes = ['linear', 'circle', 'elastic', 'bounce']; | |
d3.select('#ease-type') | |
.text(easetype); | |
var durationms = 300; | |
var delay = durationms * 0.8; | |
// Hide/Unhide Labels | |
var labelsHidden = false; | |
var toggleLabels = function removeLabels (delay, duration) { | |
d3.selectAll('.bar-label') | |
.transition() | |
.delay(function (d, i) { | |
return i * delay; | |
}) | |
.duration(duration) | |
.attr('y', function (d, i) { | |
console.log('label:' + d); | |
if (labelsHidden) { | |
console.log('Unhide labels'); | |
return h - yscale(d); | |
} else { | |
console.log('Hide labels'); | |
return h + 14; | |
} | |
}); | |
labelsHidden = !labelsHidden; | |
}; | |
// Interaction | |
// Bar mousing | |
var setBarMousing = function setBarMousing () { | |
var barMouseOutDuration = 300; | |
var barMouseDelay = 10; | |
d3.selectAll('rect.bar') | |
.on('click', function (d, i) { | |
console.log(d); | |
}) | |
.on('mouseover', function (d, i) { | |
// console.log('mouseover [' + i + '] ' + d); | |
var xPos = parseFloat(d3.select(this).attr('x')) + xscale.rangeBand()/2; | |
var yPos = parseFloat(d3.select(this).attr('y')) / 2 + h / 2; | |
// set tooltip position and text | |
d3.select('#tooltip') | |
.style('left', xPos + 'px') | |
.style('top', yPos + 'px') | |
.select('#value') | |
.text(d); | |
// toggle hidden off | |
d3.select('#tooltip').classed('hidden', false); | |
}) | |
.on('mouseout', function (d, i) { | |
// console.log('mouseout [' + i + '] ' + d); | |
d3.select('#tooltip').classed('hidden', true); | |
}); | |
}; | |
/** UI */ | |
var initUI = function () { | |
var easeButton, dataButton, sortButton; | |
var dataButton = d3.select('#controls') | |
.append('button') | |
.attr('class', 'data-reload'); | |
var easeButton = d3.select('#controls') | |
.append('button') | |
.attr('class', 'ease-type'); | |
var sortButton = d3.select('#controls') | |
.append('button') | |
.attr('class', 'sort-type'); | |
// Cycle through ease types | |
easeButton.on('click', function () { | |
alleasetypes.push(easetype); | |
easetype = alleasetypes.shift(); | |
easeButton.text(easetype); | |
}); | |
// Randomly generate new data | |
var sayings = ['hey yo', 'yo yo', 'yo yo']; | |
var barChangeDur = 200; | |
var barChangeDelay = 20; | |
dataButton. | |
on('click', function () { | |
dataset = dynamicData(dataset.length, maxvalue); | |
console.log('Regenerating dataset:'); console.log(dataset); | |
updateYScale(); | |
// Bars | |
svg.selectAll('rect') | |
.data(dataset) | |
.transition() | |
.delay(function (d, i) { | |
return i * barChangeDelay; | |
}) | |
.duration(barChangeDur) | |
.ease(easetype) | |
.attr('x', function (d, i) { | |
return xscale(i); | |
}) | |
.attr('y', function (d) { | |
return h - yscale(d); | |
}) | |
.attr('width', w / dataset.length - barPadding) | |
.attr('height', function (d) { | |
return yscale(d); | |
}) | |
.attr('fill', function (d) { | |
return barColor(d); | |
}); | |
// Labels | |
svg.selectAll('text') | |
.data(dataset) | |
.transition() | |
.delay(function (d, i) { | |
return i * barChangeDelay; | |
}) | |
.duration(barChangeDur) | |
.ease(easetype) | |
.text(function (d) { | |
return d; | |
}) | |
.attr('text-anchor', 'middle') | |
.attr('x', function (d, i) { | |
return xscale(i) + xscale.rangeBand() / 2; | |
}) | |
.attr('y', function (d) { | |
return h - yscale(d) + 14; | |
}) | |
.attr('font-family', 'sans-serif') | |
.attr('font-size', '11px') | |
.attr('fill', 'white'); | |
}); | |
// Sort bars | |
var barSortDuration = 1000; | |
var barSortDelay = 0; | |
var sortOrder = false; | |
var sorttype = 'Ascending'; | |
var sortAll = function sortAll() { | |
} | |
var sortBars = function sortBars() { | |
d3.selectAll('rect') | |
.sort(function (a,b) { | |
if (sortOrder) { | |
return d3.ascending(a, b); | |
} else { | |
return d3.descending(a, b); | |
} | |
}) | |
.transition() | |
.duration(1000) | |
.attr('x', function(d, i) { | |
return xscale(i); | |
}); | |
}; | |
var sortLabels = function sortLabels(animate, duration, delay) { | |
console.log('sort labels'); | |
if (!animate) { | |
duration = 0; | |
delay = 0; | |
} else { | |
if (null == duration) duration = defaultDuration; | |
if (null == delay) delay = defaultDelay; | |
} | |
d3.selectAll('.bar-label') | |
.sort(function (a, b) { | |
if (sortOrder) { | |
return d3.ascending(a, b); | |
} else { | |
return d3.descending(a, b); | |
} | |
}) | |
.attr('x', function (d, i) { | |
return xscale(i) + xscale.rangeBand() / 2; | |
}) | |
.attr('y', function (d) { | |
return h - yscale(d) + 14; | |
}); | |
}; | |
sortButton.on('click', function () { | |
sortOrder = !sortOrder; | |
// toggleLabels(true, medDuration, medDelay); | |
sortBars(); | |
sortLabels(); | |
// toggleLabels(true, medDuration, medDelay); | |
// update sort button text | |
d3.select('button.sort') | |
.text(function () { | |
if (sortOrder) { | |
return 'Descending'; | |
} | |
return 'Ascending'; | |
}); | |
}); | |
dataButton.text('Reload Data') | |
easeButton.text(easetype); | |
sortButton.text('Ascending'); | |
}; | |
// Run | |
function updateChart() { | |
createSVG(); | |
initUI(); | |
updateScales(); | |
updateBars(true, medDuration, medDelay); | |
setBarMousing(); | |
updateLabels(true, longDuration, medDelay); | |
} | |
updateChart(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment