Going overboard on features
Last active
December 15, 2015 01:59
-
-
Save ne8il/5184165 to your computer and use it in GitHub Desktop.
D3 Example 5 : Transitions! Exits!
This file contains hidden or 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> | |
<meta charset="utf-8"> | |
<head> | |
<style> | |
body { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
width: 960px; | |
padding:20px; | |
position: relative; | |
} | |
ul { | |
list-style: none; | |
} | |
.peopleDiv { | |
display:inline-block; | |
position:relative; | |
height:30px; | |
line-height:30px; | |
width:100px; | |
text-align:center; | |
} | |
svg { | |
padding:20px; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: black; | |
shape-rendering: crispEdges; | |
} | |
.axis text { | |
font-family: sans-serif; | |
font-size: 11px; | |
} | |
#formArea { | |
border:1px solid #c3c3c3; | |
background-color:#f1f1f1; | |
border-radius:5px; | |
width:200px; | |
padding:10px; | |
margin:10px; | |
float:left; | |
height:240px; | |
} | |
#formArea input{ | |
display:block; | |
margin: 10px 0px; | |
} | |
button { | |
border:1px solid #a3a3a3; | |
background-color:#d3d3d3; | |
padding:5px; | |
width:100px; | |
border-radius:10px; | |
cursor: pointer; | |
} | |
button:hover { | |
background-color:#c3c3c3; | |
} | |
button:active { | |
background-color:#a3a3a3; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="svgArea"></div> | |
<div class="forms"> | |
<div id="formArea"> | |
<ul> | |
<li><label>Name<input type="text" id="nameInput"/></label></li> | |
<li><label>Weight<input type="number" min="100" max="500" id="weightInput"/></label></li> | |
<li><label>Color<input type="color" id="colorInput"/></label></li> | |
<li> <button id="addButton">Add</button></li> | |
</ul> | |
</div> | |
<div id="formArea"> | |
<ul> | |
<li><label>Name<select id="removeSelect"/> | |
</select></label></li> | |
<li> <button id="removeButton">Remove</button></li> | |
</ul> | |
</div> | |
<div id="formArea"> | |
<ul> | |
<li> <button id="shuffleButton">Shuffle</button></li> | |
<li> <button id="ascButton">Ascending</button></li> | |
<li> <button id="descButton">Descending</button></li> | |
</ul> | |
</div> | |
</div> | |
</body> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var people = [{'name' : 'Frank', 'color' : 'blue', 'weight' : 180}, | |
{'name' : 'Tom', 'color' : 'red', 'weight' : 230}, | |
{'name' : 'Peter', 'color' : 'green', 'weight' : 190}, | |
{'name' : 'Mary', 'color' : 'purple', 'weight' : 150}]; | |
var chartWidth = 800, | |
chartHeight = 250, | |
padding = 30, | |
duration = 1000; //transition length (in ms) | |
var svg = d3.select('#svgArea') | |
.append('svg') | |
.attr('width', chartWidth) | |
.attr('height', chartHeight); | |
/* | |
* Append our axis groups before rendering chart | |
*/ | |
var yAxisGroup = svg.append("g") | |
.classed('axis', true) | |
.classed('y-axis', true) | |
.attr("transform", "translate(" + padding + ",0)") | |
var xAxisGroup = svg.append("g") | |
.classed('axis', true) | |
.attr('transform', 'translate(' + padding + ', ' + chartHeight + ')'); | |
var buildChart = function(){ | |
/** SCALES */ | |
var min = d3.min(people, function(d){return d['weight']}); | |
var max = d3.max(people, function(d){return d['weight']}); | |
var yScale = d3.scale.linear() | |
.domain([0, max]) | |
.rangeRound([0, chartHeight]); | |
//populate an array of names to use for our ordinal scale | |
var nameCats = people.map(function(d){ return d['name']}); | |
var xScale = d3.scale.ordinal() | |
.domain(nameCats) | |
.rangeBands([0, chartWidth]); | |
var rect = svg.selectAll("rect") | |
.data(people, function(d) { | |
return d['name'] | |
}); | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient('bottom'); | |
xAxisGroup | |
.transition() | |
.duration(duration) | |
.call(xAxis); | |
rect.enter() | |
.insert('rect') | |
.classed('chartBlock', true) | |
.style('fill', function(d){ | |
return d['color'] | |
}) | |
.attr('x', function(d, i){ | |
return xScale(d['name']) - .5 + 2.5 + padding; | |
}) | |
.attr('y', function(d){ | |
return (chartHeight - yScale(d['weight'])) | |
}) | |
.attr('height', function(d){ | |
return yScale(d['weight']); | |
}) | |
.attr('width', chartWidth / people.length - 5); | |
//remove anything we've deleted | |
rect.exit() | |
.remove(); | |
//when transitioning, we don't have to worry about the color | |
//but everything else could have changed | |
rect.transition() | |
.duration(duration) | |
.attr("x", function(d, i) { | |
return xScale(d['name']) - .5 + 2.5 + padding; | |
}) | |
.attr('y', function(d){ | |
return (chartHeight - yScale(d['weight'])) | |
}) | |
.attr('height', function(d){ | |
return yScale(d['weight']); | |
}) | |
.attr('width', chartWidth / people.length - 5); | |
var yAxisScale = d3.scale.linear() | |
.domain([0, max]) | |
.rangeRound([chartHeight, 0]); | |
var yAxis = d3.svg.axis() | |
.scale(yAxisScale) | |
.orient('left') | |
.ticks(10); | |
yAxisGroup.transition() | |
.duration(duration) | |
.call(yAxis); | |
} | |
/** Event Handlers and Functions */ | |
/** | |
* Add person to chart | |
* Name is required | |
*/ | |
var addPerson = function(){ | |
var nameInput = d3.select('#nameInput').node(); | |
var weightInput = d3.select('#weightInput').node(); | |
var colorInput = d3.select('#colorInput').node(); | |
if(nameInput.value == '') return; | |
var weight = Math.min(Math.max(weightInput.value, 100), 500); | |
var newPerson = { | |
'name' : nameInput.value, | |
'color' : colorInput.value, | |
'weight' : weight | |
} | |
people.push(newPerson); | |
populateRemoveSelect(); | |
//clear out inputs | |
nameInput.value = ''; | |
weightInput.value = ''; | |
colorInput.value = ''; | |
//refresh chart | |
buildChart(); | |
} | |
/** | |
* remove selected person | |
*/ | |
var removePerson = function(){ | |
var removeInput = d3.select('#removeSelect').node(); | |
var name = removeInput.value; | |
//easiest way to splice this person out | |
people = people.filter(function(person){ | |
return person['name'] != name; | |
}); | |
populateRemoveSelect(); | |
buildChart(); | |
} | |
/** | |
* Fills in remove select with our names | |
*/ | |
var populateRemoveSelect = function(){ | |
var removeInput = d3.select('#removeSelect').node(); | |
//clear existing options | |
removeInput.options.length = 0; | |
people.forEach(function(name){ | |
var display = name['name']; | |
removeInput.add(new Option(display, display)); | |
}); | |
} | |
/** | |
* Randomly shuffles our people and rebuilds chart | |
*/ | |
var shuffle = function(){ | |
sortAndBuild(function(a, b){ | |
var chance = !!Math.round(Math.random() * 1); | |
return chance ? 1 : -1; | |
}); | |
} | |
/** | |
* sort in ascending order | |
*/ | |
var asc = function(){ | |
sortAndBuild(function(a, b){ | |
return a['weight'] > b['weight']; | |
}); | |
} | |
/** | |
* Randomly shuffles our people and rebuilds chart | |
*/ | |
var desc = function(){ | |
sortAndBuild(function(a, b){ | |
return a['weight'] < b['weight']; | |
}); | |
} | |
var sortAndBuild = function(sortFunction){ | |
people.sort(sortFunction); | |
populateRemoveSelect(); | |
buildChart(); | |
} | |
//event listener to add a new person | |
d3.select('#addButton') | |
.on('click', addPerson); | |
d3.select('#removeButton') | |
.on('click', removePerson); | |
d3.select('#shuffleButton') | |
.on('click', shuffle); | |
d3.select('#ascButton') | |
.on('click', asc); | |
d3.select('#descButton') | |
.on('click', desc); | |
populateRemoveSelect(); | |
//initial render of chart | |
buildChart(); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment