Skip to content

Instantly share code, notes, and snippets.

@saraquigley
Last active November 11, 2016 20:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save saraquigley/3f1edf4fc72f053bb95b to your computer and use it in GitHub Desktop.
Save saraquigley/3f1edf4fc72f053bb95b to your computer and use it in GitHub Desktop.
d3 donuts and angular directives
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<style>
.label {
font: 300 10pt 'Futura', sans-serif;
}
div.label {
}
input.slider {
width: 75%;
margin: auto;
display: block;
}
input.number {
text-align: center;
margin-left: 2px;
}
.donut {
margin: auto;
display: flex;
justify-content: center; /* align horizontal */
align-items: center; /* align vertical */
}
.chartContainer {
padding: 20px;
display: inline-block;
margin: auto;
width: 230;
}
.header {
font: 300 14pt 'Futura', sans-serif;
text-align: center;
vertical-align: middle;
}
.sub-header {
font: 100 8pt 'Futura', sans-serif;
}
.label-value {
/*display: inline;*/
font: 300 10pt 'Helvetica Neue', sans-serif;
position: relative;
margin: auto;
display: flex;
justify-content: center; /* align horizontal */
align-items: center; /* align vertical */
/*float: none;*/
}
</style>
<body>
<div ng-app="myApp">
<div ng-controller="mainCtrl">
<div class="chartContainer">
<div class="header">Residency<br><span class="sub-header">CA-RESIDENT | NON-RESIDENT</span></div>
<res-donut data="data_res" accessor="accessor_res" class="donut"></res-donut>
<div ng-repeat="datum in data_res | filter: {label:'Non-Resident'}" >
<div class="label-value">% {{datum.label}}
<input ng-model="datum.value" type="number" min="0" max="100" class="number"></input>
</div>
<div>
<input ng-model="datum.value" type="range" min="0" max="100" class="slider"></input>
</div>
</div>
</div>
<div class="chartContainer">
<div class="header">Non-Resident Mix<br><span class="sub-header">OUT-OF-STATE | INTERNATIONAL</span></div>
<oos-donut data="data_oos" accessor="accessor_oos" class="donut"></oos-donut>
<div ng-repeat="datum in data_oos | filter: {label:'International'}">
<div class="label-value">% {{datum.label}}
<input ng-model="datum.value" type="number" min="0" max="100" class="number"></input>
</div>
<div>
<input ng-model="datum.value" type="range" min="0" max="100" class="slider"></input>
</div>
</div>
</div>
<div class="chartContainer">
<div class="header">% Entry Status<br><span class="sub-header">FRESHMEN | TRANSFER</span></div>
<ent-donut data="data_ent" accessor="accessor_ent" class="donut"></ent-donut>
<div ng-repeat="datum in data_ent | filter: {label:'Freshmen'}">
<div class="label-value"> {{datum.label}}
<input ng-model="datum.value" type="number" min="0" max="100" class="number"></input>
</div>
<div>
<input ng-model="datum.value" type="range" min="0" max="100" class="slider"></input>
</div>
</div>
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp', []);
var arc = d3.svg.arc();
myApp.controller('mainCtrl', function ($scope) {
$scope.data_res = [{ label: 'CA-Residents', value: 76 },
{ label: 'Non-Residents', value: 24 }];
$scope.data_oos = [{ label: 'Out-of-State', value: 33 },
{ label: 'International', value: 67 }];
$scope.data_ent = [{ label: 'Freshmen', value: 62 },
{ label: 'Transfers', value: 38 }];
$scope.accessor_res = function(d){ return +d.value };
$scope.accessor_oos = function(d){ return +d.value };
$scope.accessor_ent = function(d){ return +d.value };
// so that our directive can know how to access the values from our data.
// $scope.accessor = function(d){ return d.value };
});
myApp.directive('resDonut', function() {
function link(scope, el, attr){
var color = d3.scale.ordinal().range(["#bd9e39","#0868ac"]);
var data = scope.data;
var width = 150;
var height = 150;
var min = Math.min(width, height);
var svg = d3.select(el[0]).append('svg');
var pie = d3.layout.pie().sort(null);
arc.outerRadius(min / 2 * 0.8)
.innerRadius(min / 2 * 0.45);
pie.value(function(d){ return +d.value ; });
svg.attr({width: width, height: height});
var g = svg.append('g')
// center the donut chart
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
// add the <path>s for each arc slice
var arcs = g.selectAll('path').data(pie(data))
.enter().append('path')
.style('stroke', 'white')
.attr('fill-opacity', 0.75)
.attr('fill', function(d, i){ return color(i); })
// store the initial angles
.each(function(d) { return this._current = d });
var labels = g.selectAll('.label').data(pie(data))
.enter().append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("class", "label")
.style("text-anchor", "middle")
.text(function(d) { return d.data.label === "CA-Residents" ? "CA-Res" : "Non-Res"; });
scope.$watch('data', function (newVal, oldVal) {
console.log("an element within `data` changed!");
var ca = newVal.filter(function(d) {return d.label === "CA-Residents";}),
nr = newVal.filter(function(d) {return d.label === "Non-Residents";}),
duration = 750;
nr[0].value = +nr[0].value;
ca[0].value = 100 - nr[0].value;
arcs.data(pie(scope.data)); //.attr('d', arc)
arcs.transition().duration(duration).attrTween('d', arcTween);
labels.data(pie(scope.data)).transition().duration(duration).attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; });
}, true);
}
return {
link: link,
restrict: 'E',
scope: { 'data': '=',
'accessor': '=' }
};
});
myApp.directive('oosDonut', function() {
function link(scope, el, attr){
var color = d3.scale.ordinal().range(["#2b8cbe","#084081"]);
var data = scope.data;
var width = 150;
var height = 150;
var min = Math.min(width, height);
var svg = d3.select(el[0]).append('svg');
var pie = d3.layout.pie().sort(null);
arc.outerRadius(min / 2 * 0.8)
.innerRadius(min / 2 * 0.45);
pie.value(function(d){ return +d.value ; });
svg.attr({width: width, height: height});
var g = svg.append('g')
// center the donut chart
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
// add the <path>s for each arc slice
var arcs = g.selectAll('path').data(pie(data))
.enter().append('path')
.style('stroke', 'white')
.attr('fill-opacity', 0.75)
.attr('fill', function(d, i){ return color(i); })
// store the initial angles
.each(function(d) { return this._current = d });
var labels = g.selectAll('.label').data(pie(data))
.enter().append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("class", "label")
.style("text-anchor", "middle")
.text(function(d) { return d.data.label === "Out-of-State" ? "OoS" : "Int"; });
scope.$watch('data', function (newVal, oldVal) {
console.log("an element within `data` changed!");
var intl = newVal.filter(function(d) {return d.label === "International";}),
oos = newVal.filter(function(d) {return d.label === "Out-of-State";}),
duration = 750;
intl[0].value = +intl[0].value;
oos[0].value = 100 - intl[0].value;
arcs.data(pie(scope.data)); //.attr('d', arc)
arcs.transition().duration(duration).attrTween('d', arcTween);
labels.data(pie(scope.data)).transition().duration(duration).attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; });
}, true);
}
return {
link: link,
restrict: 'E',
scope: { 'data': '=',
'accessor': '=' }
};
});
myApp.directive('entDonut', function() {
function link(scope, el, attr){
var color = d3.scale.ordinal().range(["#7b4173","#ce6dbd"]);
var data = scope.data;
var width = 150;
var height = 150;
var min = Math.min(width, height);
var svg = d3.select(el[0]).append('svg');
var pie = d3.layout.pie().sort(null);
arc.outerRadius(min / 2 * 0.8)
.innerRadius(min / 2 * 0.45);
pie.value(function(d){ return +d.value ; });
svg.attr({width: width, height: height});
var g = svg.append('g')
// center the donut chart
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
// add the <path>s for each arc slice
var arcs = g.selectAll('path').data(pie(data))
.enter().append('path')
.style('stroke', 'white')
.attr('fill-opacity', 0.75)
.attr('fill', function(d, i){ return color(i); })
// store the initial angles
.each(function(d) { return this._current = d });
var labels = g.selectAll('.label').data(pie(data))
.enter().append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.attr("class", "label")
.style("text-anchor", "middle")
.text(function(d) { return d.data.label === "Freshmen" ? "Fresh" : "Trans"; });
scope.$watch('data', function (newVal, oldVal) {
console.log("an element within `data` changed!");
var intl = newVal.filter(function(d) {return d.label === "Freshmen";}),
oos = newVal.filter(function(d) {return d.label === "Transfers";}),
duration = 750;
intl[0].value = +intl[0].value;
oos[0].value = 100 - intl[0].value;
arcs.data(pie(scope.data)); //.attr('d', arc)
arcs.transition().duration(duration).attrTween('d', arcTween);
labels.data(pie(scope.data)).transition().duration(duration).attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; });
}, true);
}
return {
link: link,
restrict: 'E',
scope: { 'data': '=',
'accessor': '=' }
};
});
function arcTween(a) {
// see: http://bl.ocks.org/mbostock/1346410
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment