<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Rotating Donut</title>
<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1" />
<script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
<script src="data.js"></script>
<script src="rotating_donut.js"></script>
<link rel="stylesheet" href="button.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="donuts">
<div class="donut" id="donut1"></div>
<div class="slider">
<label for="donut-size-1">Size</label>
<input type="range" class="donut-size" id="donut-size-1" title="donut-size" data-target="#donut1" value="100" max="150" />
</div>
<div class="donut" id="donut2"></div>
<div class="slider">
<label for="donut-size-2">Size</label>
<input type="range" class="donut-size" id="donut-size-2" title="donut-size" data-target="#donut2" max="150" value="150" />
</div>
</div>
<button>Randomize Data</button>
<script src="app.js"></script>
</body>
</html>
body {
display: flex;
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
height: 600px;
margin: 8px;
}
.donuts {
display: inline-block;
position: relative;
}
.donuts {
width: 300px;
text-align: right;
overflow: hidden;
}
.donut {
margin-left: auto;
}
#donut1 {
width: 200px;
height: 200px;
}
#donut2 {
width: 300px;
height: 300px;
}
.donut-label {
font-weight: bold;
}
.slider {
display: inline-block;
height: 20px;
margin: 4px auto 16px;
}
#donut-size {
margin-left: 8px;
}
if(typeof APP === 'undefined') {APP = {};}
APP.generateData = function(splice) {
'use strict';
var colors = d3.scaleOrdinal(d3.schemeCategory10),
arr = [],
i;
for (i = 1; i <= 5; i++) {
arr.push({
id: i,
value: 5 + Math.random() * 15,
color: colors(i)
});
}
if (splice) {
arr.sort(function() {return 0.5 - Math.random();})
.splice(0, Math.random() * 5);
}
return arr;
};
document.addEventListener('DOMContentLoaded', function() {
'use strict';
var donut,
events;
function build() {
donut = APP.rotatingDonut()
.thickness(0.5)
.value(function(d) {return d.value;})
.color(function(d) {return d.color;})
.key(function(d) {return d.id;})
.sort(function(a, b) {return a.id - b.id;});
}
function addToDom() {
d3.select('#donut1')
.datum(APP.generateData())
.call(donut.label, 'Smith')
.call(donut);
d3.select('#donut2')
.datum(APP.generateData())
.call(donut.label, 'Jones')
.call(donut);
}
function addListeners() {
d3.select('button').on('click', events.dataButtonClick);
d3.selectAll('.donut-size').on('change', events.resizeSliderChange);
}
events = {
dataButtonClick: function() {
d3.select('#donut1')
.datum(APP.generateData(true))
.call(donut);
d3.select('#donut2')
.datum(APP.generateData(true))
.call(donut);
},
resizeSliderChange: function() {
var target = d3.select(this).attr('data-target'),
value = this.value * 2;
d3.selectAll(target)
.call(donut.dimensions, {width: value, height: value})
.call(donut)
.style('width', value + 'px')
.style('height', value + 'px');
}
};
build();
addToDom();
addListeners();
});
if(typeof APP === 'undefined') {APP = {};}
APP.rotatingDonut = function() {
'use strict';
var o,
local;
o = {
thickness: 0.4,
value: null,
color: null,
key: null,
sort: null
};
local = {
label: d3.local(),
dimensions: d3.local()
};
function donut(group) {
group.each(render);
}
function render(data) {
var context,
dim,
pie,
arc,
segments,
segmentEnter;
if (!data) {return;}
context = d3.select(this);
dim = getDimensions(context);
pie = d3.pie()
.value(o.value)
.sort(null);
arc = d3.arc()
.outerRadius(dim.outerRadius)
.innerRadius(dim.innerRadius);
context.selectAll('svg')
.data([pie(data.sort(o.sort))])
.enter()
.append('svg')
.append('g')
.attr('class', 'group')
.append('text')
.attr('class', 'donut-label')
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'middle');
context.selectAll('svg')
.attr('width', dim.width)
.attr('height', dim.height)
.selectAll('g.group')
.attr('transform', 'translate(' + dim.width / 2 + ',' + dim.height / 2 + ')');
context.select('text.donut-label')
.text(local.label.get(context.node()));
segments = context.selectAll('svg')
.select('g.group')
.selectAll('path.segment')
.data(Object, dataAccess('key'));
segmentEnter = segments.enter()
.append('path')
.attr('class', 'segment')
.attr('fill', dataAccess('color'));
segmentEnter
.merge(segments)
.attr('d', arc);
segments.exit()
.remove();
}
function dataAccess(key) {
return function(d) {
return o[key](d.data);
};
}
function getDimensions(context) {
var thisDimensions = local.dimensions.get(context.node()) || {},
width = thisDimensions.width || context.node().getBoundingClientRect().width,
height = thisDimensions.height || context.node().getBoundingClientRect().height,
outerRadius = Math.min(width, height) / 2,
innerRadius = outerRadius * (1 - o.thickness);
return {
width: width,
height: height,
outerRadius: outerRadius,
innerRadius: innerRadius
};
}
donut.thickness = function(_) {
if (!arguments.length) {return o.thickness;}
o.thickness = _;
return donut;
};
donut.value = function(_) {
if (!arguments.length) {return o.value;}
o.value = _;
return donut;
};
donut.color = function(_) {
if (!arguments.length) {return o.color;}
o.color = _;
return donut;
};
donut.key = function(_) {
if (!arguments.length) {return o.key;}
o.key = _;
return donut;
};
donut.sort = function(_) {
if (!arguments.length) {return o.sort;}
o.sort = _;
return donut;
};
donut.dimensions = function(context, _) {
var returnArray;
if (typeof _ === 'undefined' ) {
returnArray = context.nodes()
.map(function (node) {return local.dimensions.get(node);});
return context._groups[0] instanceof NodeList ? returnArray : returnArray[0];
}
context.each(function() {local.dimensions.set(this, _);});
return donut;
};
donut.label = function(context, _) {
var returnArray;
if (typeof _ === 'undefined' ) {
returnArray = context.nodes()
.map(function (node) {return local.label.get(node);});
return context._groups[0] instanceof NodeList ? returnArray : returnArray[0];
}
context.each(function() {local.label.set(this, _);});
return donut;
};
return donut;
};
button {
background-color: #eee;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 11px;
padding: 6px 10px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
position: absolute;
right: 16px;
}
button:hover {
background-color: #ddd;
border-color: #ccc
}
button:active {
background-color: #ccc;
}
button:focus {
outline:0;
}