Skip to content

Instantly share code, notes, and snippets.

@Zhenmao
Last active December 27, 2017 11:44
Show Gist options
  • Save Zhenmao/a345870bfa32e1434b7564f9ed9d69bb to your computer and use it in GitHub Desktop.
Save Zhenmao/a345870bfa32e1434b7564f9ed9d69bb to your computer and use it in GitHub Desktop.
Line donut chart with text in the center
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Line Donut Chart</title>
<!-- D3 -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- Google Font -->
<link href="https://fonts.googleapis.com/css?family=Roboto:300" rel="stylesheet">
<style>
body {
background-color: #F5F6F7;
font-family: 'Roboto', sans-serif;
}
/***********************************************************/
/**** Make the chart container fill the page using CSS. ****/
/***********************************************************/
#chart {
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
//////////////////////////////////////////////////////////////////
//// Data ////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
var donutData = [
{id: 1, value: 500, color: '#C961A2'},
{id: 2, value: 375, color: '#2978C4'},
{id: 3, value: 125, color: '#4EE7B3'},
];
var total = 300000;
var revolving = 0.68;
var fixed = 0.32;
//////////////////////////////////////////////////////////////////
//// Chart generator /////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
// make sure d3.js is loaded
// Chart appends an SVG inside a div with id 'chart'
// Chart expects the following input arguments:
// donutData: data to draw the donut slices
// total: the total amount (e.g. 300000)
// revolving: the revolving percent (e.g. 0.62)
// fixed: the fixed percent (e.g. 0.38)
var Chart = (function(donutData, total, revolving, fixed) {
var margin, width, height, chartRadius, radius, circleRadius, fontSize, pie, arc, chartDiv, svg, pieContainer, label;
// init only run once when setting up the chart
function init() {
// starting angle for the first slice of donut chart
var startAngle = - 1/8 * 2 * Math.PI;
// small white circle radius
circleRadius = 12;
pie = d3.pie()
.sort(null)
.startAngle(startAngle)
.endAngle(2 * Math.PI + startAngle)
.value(function(d) {
return d.value;
});
chartDiv = document.getElementById('chart');
svg = d3.select(chartDiv).append('svg');
// circle dropshadow definition
var filter = svg.append('defs')
.append('filter')
.attr('id', 'drop-shadow')
.attr('height', '200%')
.attr('width', '200%');
filter.append('feGaussianBlur')
.attr('in', 'SourceAlpha')
.attr('stdDeviation', 1); // shadow blur
filter.append('feOffset')
.attr('in', 'blur')
.attr('dx', 1) // shadow offset
.attr('dy', 2) // shadow offset
.attr('result', 'offsetBlur');
var feComponentTransfer = filter.append('feComponentTransfer');
feComponentTransfer.append('feFuncA')
.attr('type', 'linear')
.attr('slope', 0.2); // shadow color darknewss
var feMerge = filter.append('feMerge');
feMerge.append('feMergeNode');
feMerge.append('feMergeNode')
.attr('in', 'SourceGraphic');
renderChart();
}
// renderChart runs every time window resizes
function renderChart() {
updateDimensions(chartDiv.clientWidth, chartDiv.clientHeight);
// radius of the donut ring
radius = chartRadius * 0.9;
svg.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
d3.selectAll("g > *").remove();
svg.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
pieContainer = svg.append('g')
.attr('transform', 'translate(' + width/2 + ',' + height/2 + ')');
arc = d3.arc()
.outerRadius(radius)
.innerRadius(radius);
pieContainer.selectAll('.arc')
.data(pie(donutData))
.enter().append('path')
.attr('class', 'arc')
.attr('d', function(d) {
return arc(d);
})
.attr('stroke', function(d) {
return d.data.color;
})
.attr('stroke-width', 3);
pieContainer.selectAll('.circle')
.data(pie(donutData))
.enter().append('circle')
.attr('class', 'circle')
.attr('cx', function(d) {
return getCirclePos(d).cx;
})
.attr('cy', function(d) {
return getCirclePos(d).cy;
})
.attr('r', circleRadius)
.attr('fill', '#fff')
.attr('filter', 'url(#drop-shadow)');
label = pieContainer.append('text')
.attr('text-anchor', 'middle');
label.append('tspan')
.attr('id', 'total-title')
.attr('x', 0)
.attr('y', -radius/3)
.attr('font-size', 1.5 * fontSize)
.text('Total Amount Requested');
label.append('tspan')
.attr('id', 'total')
.attr('x', 0)
.attr('y', -radius/3 + 3 * fontSize)
.attr('font-size', 2.5 * fontSize)
.text('$' + d3.format(',')(total));
label.append('tspan')
.attr('id', 'revolving-title')
.attr('x', -radius/3)
.attr('y', radius/3 + 2 * fontSize)
.attr('font-size', 1 * fontSize)
.text('Revolving');
label.append('tspan')
.attr('id', 'revolving')
.attr('x', -radius/3)
.attr('y', radius/3)
.attr('font-size', 2 * fontSize)
.text(Math.round(revolving * 100) + '%');
label.append('tspan')
.attr('id', 'fixed-title')
.attr('x', radius/3)
.attr('y', radius/3 + 2 * fontSize)
.attr('font-size', 1 * fontSize)
.text('Fixed');
label.append('tspan')
.attr('id', 'fixed')
.attr('x', radius/3)
.attr('y', radius/3)
.attr('font-size', 2 * fontSize)
.text(Math.round(fixed * 100) + '%');
}
function updateDimensions(clientWidth, clientHeight) {
margin = {top: 10, bottom: 10, right: 10, left: 10};
width = Math.max(clientWidth - margin.left - margin.right, 240);
height = Math.max(clientHeight - margin.top - margin.bottom, 240);
chartRadius = Math.min(width, height) / 2;
fontSize = chartRadius > 200 ? 16 : 12;
}
function getCirclePos(d) {
var theta = d.startAngle - 1/2 * Math.PI,
r = radius;
return {cx: r * Math.cos(theta), cy: r * Math.sin(theta)};
}
init();
return {
render: renderChart
};
})(donutData, total, revolving, fixed);
//////////////////////////////////////////////////////////////////
//// Listener for resizing ///////////////////////////////////////
//////////////////////////////////////////////////////////////////
// Resize chart when window size changes
window.addEventListener('resize', Chart.render);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment