Skip to content

Instantly share code, notes, and snippets.

@timproDev
Created March 9, 2018 17:13
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/16c474af603d4e73e18fcaf385c89528 to your computer and use it in GitHub Desktop.
Save timproDev/16c474af603d4e73e18fcaf385c89528 to your computer and use it in GitHub Desktop.
Single Value Donut Chart
document.addEventListener("DOMContentLoaded", function(){
// set global variables
var path = '';
var dataFile = ['placeholder-dataset.csv','placeholder-two.csv'];
var dataFileTwo = 'placeholder-two.csv';
var elemContainer = ['#donutChart', '#donutChartTwo', '#donutChartThree','#donutChartFour'];
var uiEl = '.chart-ui-container input';
var cssFile = 'donut-chart-single.css';
var strNumHeaders = ['header_two_amount','header_three_amount']; // to convert to numbers
// instantiate constructor methods
var donutChartSingle = new DonutChartSingle({
elem:document.querySelector(elemContainer[2]),
dataCount:'header_two_amount',
transSpeed:350
});
var donutChartSingleTwo = new DonutChartSingle({
elem:document.querySelector(elemContainer[3]),
dataCount:'header_two_amount',
transSpeed:350
});
// fire chart and events
// chartInit(instance, element, dataPath)
chartInitSingle(donutChartSingle, elemContainer[2], dataFile[0], ['header_one_name', 'Chocolate']);
chartInitSingle(donutChartSingleTwo, elemContainer[3], dataFile[1], ['header_one_name', 'Carrots']);
styleLink();
function chartInitSingle(chartInst, elem, dataFile, valArr) {
d3.csv((path + dataFile), function(data) {
data.forEach(function(d) {
for (var i = 0; i < strNumHeaders.length; i++) {
d[strNumHeaders[i]] = +d[strNumHeaders[i]] || 0;
}
});
makeItFirst(data, valArr[0], valArr[1]);
chartInst.setData(data);
// ui events
var inputs = document.querySelectorAll(elem + ' ' + uiEl);
for (var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener('click', function(){
var value = this.value;
console.log(data)
chartInst.setDataCount(value);
});
}
function makeItFirst(data, column, element) {
var index = data.map(function(d){ return d[column] }).indexOf(element);
var lastIndex = data.map(function(d){ return d[column] }).lastIndexOf(element) + 1;
data.unshift(data[index]);
if (lastIndex > -1) {
data.splice(lastIndex, 1);
}
return data;
}
});
}
// include resize event
d3.select(window).on('resize', function(){
donutChartSingle.resize();
donutChartSingleTwo.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;
}
.value-percentage {
font-weight: bold;
}
var DonutChartSingle = function(opts){
this.transSpeed = opts.transSpeed;
this.dataCount = opts.dataCount;
this.colorScheme = d3.schemeCategory20b || this.colorScheme; // set up customize
this.element = opts.elem.querySelector('.plot-wrap.w-svg');
}
DonutChartSingle.prototype.getPercentage = function(){
var total = [];
for (var i = 0; i < this.data.length; i++) {
total.push(this.data[i][this.dataCount]);
}
// console.log(total)
var sum = total.reduce(add, 0);
function add(a, b) {
return a + b;
}
// console.log(sum)
var result = (this.data[0][this.dataCount] * 100) / sum;
result = Math.round(result)
// console.log(result.toString() + '%');
return result.toString() + '%';
}
DonutChartSingle.prototype.setData = function(newData){
this.data = newData;
this.draw();
}
DonutChartSingle.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.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.tau = 2 * Math.PI;
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() // for labels
.innerRadius(this.radius * 0.9)
.outerRadius(this.radius * 0.9);
this.backgroundDonut = this.plot.append("path")
.datum({endAngle: this.tau})
.style("fill", "#f4f4f4")
.attr("d", this.arc.startAngle(0));
this.createDonut()
}
DonutChartSingle.prototype.setDataCount = function(newData){
this.dataCount = newData;
this.updateData();
}
DonutChartSingle.prototype.updateData = function(opts, elem){
var self = this
this.plot = d3.select(this.element).select('svg').select('g');
this.path = this.plot.selectAll('path.slice-single').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.value-percentage")
.text(this.getPercentage());
}
DonutChartSingle.prototype.createDonut = function() {
var self = this
this.pie = d3.pie()
// .value(function(d,i) {return d[self.dataCount];})
.value(function(d,i) {return d[self.dataCount];})
.sort(null);
this.path = this.plot.selectAll("path.slice-single")
.data(this.pie(this.data));
this.path
.enter()
.append("path")
.attr("class", "slice-single")
.attr("fill", function(d, i) {
// if not the first value, no fill
if (i <= 0) {
return self.color(i);
} else {
return 'transparent';
}
})
.attr('d', this.arc)
.each(function(d){this._current = d;});
this.plot
.append('text.value-percentage')
.style('font-size',this.height / 5)
.attr("fill", function(d, i) {
return self.color(i);
})
.text(this.getPercentage())
.style('text-anchor','middle')
.attr('transform','translate(0,' + (this.height / 15) + ')');
}
DonutChartSingle.prototype.resize = function(){
console.log("single")
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>D3 Factory | D3 Libs Dependency Page</title>
<link rel="stylesheet" href="assets/css/app.css">
</head>
<body><script id="__bs_script__">//<![CDATA[
document.write("<script async src='/browser-sync/browser-sync-client.js?v=2.23.5'><\/script>".replace("HOST", location.hostname));
//]]></script>
<div style="text-align: center;">
<section style="width:40%; margin:5rem 0; display: inline-block;">
<div class="mx-dv-container" id="donutChartThree">
<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>
<section style="width:40%; margin:5rem 0; display: inline-block;">
<div class="mx-dv-container" id="donutChartFour">
<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-single.js"></script>
<script src="donut-chart-single-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 44 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 93 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