Skip to content

Instantly share code, notes, and snippets.

@timproDev
Last active March 5, 2018 19:15
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 timproDev/990395af116b4c650f577215d5340e61 to your computer and use it in GitHub Desktop.
Save timproDev/990395af116b4c650f577215d5340e61 to your computer and use it in GitHub Desktop.
Donut Chart with Radio Buttons
document.addEventListener("DOMContentLoaded", function(){
// set global variables
var path = '';
var dataFile = 'placeholder-dataset.csv';
var dataFileTwo = 'placeholder-two.csv';
var elemContainer = ['#donutChart', '#donutChartTwo'];
var uiEl = '.chart-ui-container input';
var cssFile = 'donut-chart.css';
var strNumHeaders = ['header_two_amount','header_three_amount']; // to convert to numbers
// instantiate constructor methods
var donutChart = new DonutChart({
elem:document.querySelector(elemContainer[0]),
dataCount:'header_two_amount',
data: path + dataFile,
transSpeed:350
});
// instantiate constructor methods
var donutChartTwo = new DonutChart({
elem:document.querySelector(elemContainer[1]),
dataCount:'header_two_amount',
data: path + dataFileTwo,
transSpeed:350
});
// fire chart and events
chartInit(donutChart, '#donutChart', dataFile);
chartInit(donutChartTwo, '#donutChartTwo', dataFileTwo);
styleLink();
function chartInit(chartInst, elem, dataFile){
d3.csv((path + dataFile), function(data) {
// convert defined header values to number
data.forEach(function(d) {
for (var i = 0; i < strNumHeaders.length; i++) {
d[strNumHeaders[i]] = +d[strNumHeaders[i]] || 0;
}
});
chartInst.setData(data);
var inputs = document.querySelectorAll(elem + ' ' + uiEl);
for (var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener('click', function(){
var value = this.value;
chartInst.setDataCount(value);
});
}
});
}
// include resize event
d3.select(window).on('resize', function(){
donutChart.resize();
donutChartTwo.resize();
});
// inject custom chart css
function styleLink(){
var linkElem = document.createElement('link');
document.getElementsByTagName('head')[0].appendChild(linkElem);
linkElem.rel = 'stylesheet';
linkElem.type = 'text/css';
linkElem.href = (path + cssFile);
}
});
* {
font-family: arial, verdana, sans-serif;
color:#808080;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
line {
shape-rendering: crispEdges;
}
.tooltip {
box-shadow: 0 2px 4px 0px rgba(0, 0, 0, 0.5);
border-radius: 2px;
background-color: #fff;
top: -1000px;
position: fixed;
padding: .65rem;
pointer-events: none;
max-width: 25%;
z-index: 500;
}
.tooltip-info {
padding: 0;
margin: 0;
display: block;
width: 100%;
}
.tooltip-hidden {
opacity: 0;
transition: all .3s;
transition-delay: .1s;
}
.chart-ui-container {
display: block;
width: 200px;
margin:0 auto;
padding: 2rem 0 0 0;
}
.chart-ui-container .chart-form__radio {}
.chart-ui-container .chart-form__radio-label {
display: inline-block;
padding: 0 .5rem;
border-radius: 3px;
font-size: .8rem;
}
.chart-ui-container .chart-form__radio-input {
margin-right: .75rem;
cursor: pointer;
outline:none;
}
.legend text {
font-size:.65rem;
}
var DonutChart = function(opts){
this.transSpeed = opts.transSpeed;
this.data = opts.data;
this.dataCount = opts.dataCount;
this.colorScheme = d3.schemeCategory20b || this.colorScheme; // set up customize
this.element = opts.elem.querySelector('.plot-wrap.w-svg');
this.draw();
}
DonutChart.prototype.setData = function(newData){
this.data = newData;
this.draw();
}
DonutChart.prototype.setDataCount = function(newData){
this.dataCount = newData;
this.updateData();
}
DonutChart.prototype.draw = function(){
var self = this
this.margin = {
top:0,
left:0,
right:0,
bottom:0
};
this.width = this.element.offsetWidth - this.margin.left - this.margin.right;
this.width = parseInt(d3.select(this.element).style('width'), 10);
this.height = this.width / 2 - (this.margin.top - this.margin.bottom);
this.element.innerHTML = '';
var svg = d3.select(this.element).append('svg');
svg.attr('width', this.width + (this.margin.left + this.margin.right));
svg.attr('height', (this.height + this.margin.top + this.margin.bottom));
this.plot = svg.append('g')
.attr("transform", "translate(" + this.width / 2 + "," + this.height / 2 + ")");
this.createDonut()
}
DonutChart.prototype.createDonut = function() {
var self = this
this.radius = Math.min(this.width, this.height) / 2;
this.ringSize = this.height / 5;
this.colorRange = d3.scaleOrdinal(this.colorScheme);
this.color = d3.scaleOrdinal()
.range(this.colorRange.range());
this.arc = d3.arc()
.innerRadius(this.radius)
.outerRadius(this.radius - this.ringSize);
this.outerArc = d3.arc()
.innerRadius(this.radius * 0.9)
.outerRadius(this.radius * 0.9);
this.pie = d3.pie()
.value(function(d,i) {return d[self.dataCount];})
.sort(null);
this.path = this.plot.selectAll("path.slice")
.data(this.pie(this.data));
this.path
.enter()
.append("path")
.attr("class", "slice")
.attr("fill", function(d, i) { return self.color(i); })
.attr('d', this.arc)
.each(function(d){this._current = d;});
this.text = this.plot.selectAll("text")
.data(this.pie(this.data))
.enter()
.append("text")
.attr("dy", ".35em")
.style("opacity", function(d){
return d.data[self.dataCount] <= 0 ? "0" : "1";
})
.text(function(d) {
return d.data['header_one_name'];
})
.each(function(d) {
this._current = d;
})
.transition().duration(this.transSpeed)
.attrTween("transform", function(d) {
var interpolate = d3.interpolate(this._current, d);
var _this = this;
return function(t) {
var d2 = interpolate(t);
_this._current = d2;
var pos = self.outerArc.centroid(d2);
pos[0] = self.radius * (self.midAngle(d2) < Math.PI ? 1 : -1);
return "translate("+ pos +")";
};
})
.styleTween("text-anchor", function(d){
var interpolate = d3.interpolate(this._current, d);
return function(t) {
var d2 = interpolate(t);
return self.midAngle(d2) < Math.PI ? "start":"end";
};
});
this.polyline = this.plot.selectAll("polyline")
.data(this.pie(this.data))
.enter()
.append("polyline")
.style("opacity", function(d){
return d.data[self.dataCount] <= 0 ? "0" : "1";
})
.attr('fill','none')
.attr('stroke-width','1')
.attr('stroke','#bfbfbf')
.transition().duration(this.transSpeed)
.attrTween("points", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = self.outerArc.centroid(d2);
pos[0] = self.radius * 0.95 * (self.midAngle(d2) < Math.PI ? 1 : -1);
return [self.arc.centroid(d2), self.outerArc.centroid(d2), pos];
};
});
this.midAngle = function(d){
return d.startAngle + (d.endAngle - d.startAngle)/2;
}
}
DonutChart.prototype.updateData = function(opts, elem){
var self = this
//this.data = opts.data;
//this.element = document.querySelector(elem).querySelector('.plot-wrap.w-svg');
this.plot = d3.select(this.element).select('svg').select('g');
//this.dataCount = opts.dataCount; // here pie will reference the new data
this.path = this.plot.selectAll('path.slice').data(this.pie(this.data));
this.path
.transition().duration(this.transSpeed)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return self.arc(interpolate(t));
};
});
this.newText = this.plot.selectAll("text")
.data(this.pie(this.data))
.transition().duration(this.transSpeed)
.attrTween("transform", function(d) {
var interpolate = d3.interpolate(this._current, d);
var _this = this;
return function(t) {
var d2 = interpolate(t);
_this._current = d2;
var pos = self.outerArc.centroid(d2);
pos[0] = self.radius * (self.midAngle(d2) < Math.PI ? 1 : -1);
return "translate("+ pos +")";
};
})
.style("opacity", function(d){
return d.data[self.dataCount] <= 0 ? "0" : "1";
})
.styleTween("text-anchor", function(d){
var interpolate = d3.interpolate(this._current, d);
return function(t) {
var d2 = interpolate(t);
return self.midAngle(d2) < Math.PI ? "start":"end";
};
});
this.newPolyline = this.plot.selectAll("polyline")
.data(this.pie(this.data))
.attr('fill','none')
.attr('stroke-width','1')
.attr('stroke','#bfbfbf')
.transition().duration(this.transSpeed)
.style("opacity", function(d){
return d.data[self.dataCount] <= 0 ? "0" : "1";
})
.attrTween("points", function(d){
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
var d2 = interpolate(t);
var pos = self.outerArc.centroid(d2);
pos[0] = self.radius * 0.95 * (self.midAngle(d2) < Math.PI ? 1 : -1);
return [self.arc.centroid(d2), self.outerArc.centroid(d2), pos];
};
});
}
DonutChart.prototype.resize = function(){
this.width = parseInt(d3.select(this.element).style('width'), 10);
this.draw();
}
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Donut Chart</title>
</head>
<body>
<div style="text-align: center;">
<section style="width:40%; margin:5rem 0; display: inline-block;">
<div class="mx-dv-container" id="donutChart">
<div class="plot-wrap w-svg"></div>
<div class="plot-wrap no-svg">
<!-- <img src="assets/d3-bundles/horiz-barchart.png"> -->
</div>
<form class="chart-ui-container">
<div class="chart-form__radio">
<label class="chart-form__radio-label"><input class="chart-form__radio-input" type="radio" name="dataset" value="header_two_amount" checked>Header Two Amount</label>
</div>
<div class="chart-form__radio">
<label class="chart-form__radio-label"><input class="chart-form__radio-input" type="radio" name="dataset" value="header_three_amount">Header Three Amount</label>
</div>
</form>
</div>
</section>
<section style="width:40%; margin:5rem 0; display: inline-block;">
<div class="mx-dv-container" id="donutChartTwo">
<div class="plot-wrap w-svg"></div>
<div class="plot-wrap no-svg"></div>
<form class="chart-ui-container">
<div class="chart-form__radio">
<label class="chart-form__radio-label"><input class="chart-form__radio-input" type="radio" name="dataset" value="header_two_amount" checked>Header Two Amount</label>
</div>
<div class="chart-form__radio">
<label class="chart-form__radio-label"><input class="chart-form__radio-input" type="radio" name="dataset" value="header_three_amount">Header Three Amount</label>
</div>
</form>
</div>
</section>
</div>
<script src="donut-chart.js"></script>
<script src="donut-chart-init.js"></script>
<script src="https://rawgit.com/timproDev/d3-first-timer/master/js/d3v4-473-jetpack.js"></script>
</body>
</html>
header_one_name header_two_amount header_three_amount header_four_text_string header_five_date
Glazed 50 faucibus 30/04/2013
Chocolate 64 444 viverra 20/11/2017
Cinnamon Sugar 30 76 purus 15/11/2015
Jelly 73 sed 27/01/2015
Marble Frosted 95 495 amet 17/02/2015
Apple Crumb 97 367 tellus 27/11/2013
header_one_name header_two_amount header_three_amount header_four_text_string header_five_date
Apples 12 faucibus 30/04/2013
Bananas 88 144 viverra 20/11/2017
Grapes 54 376 purus 15/11/2015
Pears 21 sed 27/01/2015
Carrots 33 299 amet 17/02/2015
Celery 11 245 tellus 27/11/2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment