Skip to content

Instantly share code, notes, and snippets.

@eesur
Last active November 26, 2017 19:36
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 eesur/164f6fd5d563178e99701378a31ac09c to your computer and use it in GitHub Desktop.
Save eesur/164f6fd5d563178e99701378a31ac09c to your computer and use it in GitHub Desktop.
d3 | reusable single value donut chart
var d3 = window.d3
// hook on with a temp namespace
d3.eesur = {}
d3.eesur.singleValueDonut = function createDonut () {
// getter/setters
var height = null
var width = null
var innnerRadius = 45
var outerRadius = 90
var colors = ['#f43530', '#eee']
function exports (selection) {
selection.each(function (datum) {
var remainingPercent = 100 - datum.value
var pies = d3.pie().sort(null)([datum.value, remainingPercent])
// use the DOM container as the default width
if (width === null) {
width = +d3.select(this).style('width').slice(0, -2)
}
var arc = d3.arc()
.innerRadius(innnerRadius)
.outerRadius(outerRadius)
.startAngle(function (d) { return d.startAngle; })
.endAngle(function (d) { return d.endAngle; })
// add the title
d3.select(this).append('h5')
.attr('class', 'center caps')
.text(datum.subject)
// add the svg
var svg = d3.select(this)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', ("translate(" + (width / 2) + ", " + (height / 2) + ")"))
svg.selectAll('path')
.data(pies)
.enter().append('path')
.attr('d', arc)
.attr('fill', function (d) { return colors[d.index]; })
// add
svg.append('text')
.attr('transform', "translate(-15, 5)")
.text(datum.value + '%')
})
}
exports.height = function (value) {
if (!arguments.length) { return height }
height = value
return this
}
exports.width = function (value) {
if (!arguments.length) { return width }
width = value
return this
}
exports.innnerRadius = function (value) {
if (!arguments.length) { return innnerRadius }
innnerRadius = value
return this
}
exports.outerRadius = function (value) {
if (!arguments.length) { return outerRadius }
outerRadius = value
return this
}
exports.colors = function (value) {
if (!arguments.length) { return colors }
colors = value
return this
}
return exports
}

Prototype of using a reusable single value donut chart

to create a single chart:

    const donut = d3.eesur.singleValueDonut()
        .width(200)
        .height(200)

    d3.select('#vis')
        .datum({
            subject: 'Showing x',
            value : 75  
        })
        .call(donut)

the example use a loop to render multiple charts using the reusable component. The component takes the following api options:

singleValueDonut()
    .height(200) // number
    .width(200) // number or null, then will default to dom container
    .innnerRadius(45) // number
    .outerRadius(90) // number
    .colors(['#f43530', '#eee']) // array of two hex values
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- http://www.basscss.com/ -->
<link href="https://unpkg.com/basscss@8.0.2/css/basscss.min.css" rel="stylesheet">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, Consolas, monaco, monospace;}
.max-width-900 {
max-width: 900px;
}
</style>
</head>
<body>
<header class="max-width-900 mx-auto mb4">
<p class="max-width-2">Prototype for showing an alternative to standard pie/donut, by using only one value per donut for ease of reading</p>
</header>
<div class="flex flex-wrap max-width-900 mx-auto js-donuts"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- d3 code -->
<script src=".script-compiled.js" charset="utf-8"></script>
<!-- render code -->
<script>
(function () {
const d3 = window.d3
const data = [
{
subject: 'Subject A',
value: 50
},
{
subject: 'Subject B',
value: 20
},
{
subject: 'Subject C',
value: 18
},
{
subject: 'Subject D',
value: 12
}
]
data.forEach(function (d, i) {
let charts = []
// create the dom
d3.select('.js-donuts').append('div')
.attr('class', 'col-3 ref-' + i)
// render the charts
charts[i] = d3.eesur.singleValueDonut()
.height(200)
d3.select('.ref-' + i)
.datum(d)
.call(charts[i])
})
}())
</script>
</body>
</html>
const d3 = window.d3
// hook on with a temp namespace
d3.eesur = {}
d3.eesur.singleValueDonut = function createDonut () {
// getter/setters
let height = null
let width = null
let innnerRadius = 45
let outerRadius = 90
let colors = ['#f43530', '#eee']
function exports (selection) {
selection.each(function (datum) {
// set the reaming from a 100% total
const remainingPercent = 100 - datum.value
const pies = d3.pie().sort(null)([datum.value, remainingPercent])
// use the DOM container as the default width
if (width === null) {
width = +d3.select(this).style('width').slice(0, -2)
}
const arc = d3.arc()
.innerRadius(innnerRadius)
.outerRadius(outerRadius)
.startAngle(d => d.startAngle)
.endAngle(d => d.endAngle)
// add the title
d3.select(this).append('h5')
.attr('class', 'center caps')
.text(datum.subject)
// add the svg
const svg = d3.select(this)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', `translate(${width / 2}, ${height / 2})`)
svg.selectAll('path')
.data(pies)
.enter().append('path')
.attr('d', arc)
.attr('fill', d => colors[d.index])
// add a label in the center
svg.append('text')
.attr('transform', `translate(-15, 5)`)
.text(datum.value + '%')
})
}
exports.height = function (value) {
if (!arguments.length) return height
height = value
return this
}
exports.width = function (value) {
if (!arguments.length) return width
width = value
return this
}
exports.innnerRadius = function (value) {
if (!arguments.length) return innnerRadius
innnerRadius = value
return this
}
exports.outerRadius = function (value) {
if (!arguments.length) return outerRadius
outerRadius = value
return this
}
exports.colors = function (value) {
if (!arguments.length) return colors
colors = value
return this
}
return exports
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment