Last active
November 26, 2015 12:41
-
-
Save schaloner/b16be91d1a92edce784d to your computer and use it in GitHub Desktop.
A combination of a bar chart and a pie chart for Chartist.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(window, document, Chartist) { | |
'use strict'; | |
/** | |
* Default options in line charts. Expand the code view to see a detailed list of options with comments. | |
* | |
* @memberof Chartist.Radial | |
*/ | |
var defaultOptions = { | |
// Specify a fixed width for the chart as a string (i.e. '100px' or '50%') | |
width: undefined, | |
// Specify a fixed height for the chart as a string (i.e. '100px' or '50%') | |
height: undefined, | |
// Padding of the chart drawing area to the container element and labels as a number or padding object {top: 5, right: 5, bottom: 5, left: 5} | |
chartPadding: 5, | |
// Override the class names that are used to generate the SVG structure of the chart | |
classNames: { | |
chartRadial: 'ct-chart-radial', | |
series: 'ct-series', | |
sliceRadial: 'ct-slice-radial', | |
label: 'ct-label' | |
}, | |
maxValue: 100, | |
startAngle: 0, | |
showLabel: true, | |
labelOffset: 0, | |
labelPosition: 'outside', | |
labelInterpolationFnc: Chartist.noop, | |
// Label direction can be 'neutral', 'explode' or 'implode'. The labels anchor will be positioned based on those settings as well as the fact if the labels are on the right or left side of the center of the chart. Usually explode is useful when labels are positioned far away from the center. | |
labelDirection: 'neutral' | |
}; | |
/** | |
* Determines SVG anchor position based on direction and center parameter | |
* | |
* @param center | |
* @param label | |
* @param direction | |
* @return {string} | |
*/ | |
function determineAnchorPosition(center, label, direction) { | |
var toTheRight = label.x > center.x; | |
if(toTheRight && direction === 'explode' || | |
!toTheRight && direction === 'implode') { | |
return 'start'; | |
} else if(toTheRight && direction === 'implode' || | |
!toTheRight && direction === 'explode') { | |
return 'end'; | |
} else { | |
return 'middle'; | |
} | |
} | |
/** | |
* Creates the radial chart | |
* | |
* @param options | |
*/ | |
function createChart(options) { | |
var seriesGroups = [], | |
labelsGroup, | |
chartRect, | |
radius, | |
labelRadius, | |
totalDataSum, | |
startAngle = options.startAngle, | |
dataArray = Chartist.getDataArray(this.data, false); | |
// Create SVG.js draw | |
this.svg = Chartist.createSvg(this.container, options.width, options.height,options.classNames.chartRadial); | |
// Calculate charting rect | |
chartRect = Chartist.createChartRect(this.svg, options, defaultOptions.padding); | |
// Get biggest circle radius possible within chartRect | |
radius = Math.min(chartRect.width() / 2, chartRect.height() / 2); | |
// Calculate total of all series to get reference value or use total reference from optional options | |
totalDataSum = dataArray.length; | |
// If labelPosition is set to `outside` the label position is at the radius, | |
// if regular radial chart it's half of the radius | |
if(options.labelPosition === 'outside') { | |
labelRadius = radius; | |
} else if(options.labelPosition === 'center') { | |
// If labelPosition is center we start with 0 and will later wait for the labelOffset | |
labelRadius = 0; | |
} else { | |
// Default option is 'inside' where we use half the radius so the label will be placed in the center of the radial | |
// slice | |
labelRadius = radius / 2; | |
} | |
// Add the offset to the labelRadius where a negative offset means closed to the center of the chart | |
labelRadius += options.labelOffset; | |
// Calculate end angle based on total sum and current data value and offset with padding | |
var center = { | |
x: chartRect.x1 + chartRect.width() / 2, | |
y: chartRect.y2 + chartRect.height() / 2 | |
}; | |
// Check if there is only one non-zero value in the series array. | |
var hasSingleValInSeries = this.data.series.filter(function(val) { | |
return val.hasOwnProperty('value') ? val.value !== 0 : val !== 0; | |
}).length === 1; | |
//if we need to show labels we create the label group now | |
if(options.showLabel) { | |
labelsGroup = this.svg.elem('g', null, null, true); | |
} | |
// Draw the series | |
// initialize series groups | |
for (var i = 0; i < this.data.series.length; i++) { | |
var series = this.data.series[i]; | |
seriesGroups[i] = this.svg.elem('g', null, null, true); | |
// If the series is an object and contains a name or meta data we add a custom attribute | |
seriesGroups[i].attr({ | |
'series-name': series.name | |
}, Chartist.xmlNs.uri); | |
// Use series class from series data or if not set generate one | |
seriesGroups[i].addClass([ | |
options.classNames.series, | |
(series.className || options.classNames.series + '-' + Chartist.alphaNumerate(i)) | |
].join(' ')); | |
var endAngle = startAngle + ((1 / totalDataSum) * 360); | |
// If we need to draw the arc for all 360 degrees we need to add a hack where we close the circle | |
// with Z and use 359.99 degrees | |
if(endAngle - startAngle === 360) { | |
endAngle -= 0.01; | |
} | |
var itemRadius = radius * (dataArray[i]/options.maxValue); | |
var start = Chartist.polarToCartesian(center.x, center.y, itemRadius, startAngle - (i === 0 || hasSingleValInSeries ? 0 : 0.2)), | |
end = Chartist.polarToCartesian(center.x, center.y, itemRadius, endAngle); | |
// Create a new path element for the radial chart. If this isn't a donut chart we should close the path for a correct stroke | |
var path = new Chartist.Svg.Path(true) | |
.move(end.x, end.y) | |
.arc(itemRadius, itemRadius, 0, endAngle - startAngle > 180, 0, start.x, start.y); | |
// If regular radial chart (no donut) we add a line to the center of the circle for completing the radial | |
path.line(center.x, center.y); | |
// Create the SVG path | |
// If this is a donut chart we add the donut class, otherwise just a regular slice | |
var pathElement = seriesGroups[i].elem('path', { | |
d: path.stringify() | |
}, options.classNames.sliceRadial); | |
// Adding the radial series value to the path | |
pathElement.attr({ | |
'value': dataArray[i], | |
'meta': this.data.labels ? this.data.labels[i] : '' | |
}, Chartist.xmlNs.uri); | |
// Fire off draw event | |
this.eventEmitter.emit('draw', { | |
type: 'slice', | |
value: 1, | |
totalDataSum: totalDataSum, | |
index: i, | |
meta: series.meta, | |
series: series, | |
group: seriesGroups[i], | |
element: pathElement, | |
path: path.clone(), | |
center: center, | |
radius: radius, | |
startAngle: startAngle, | |
endAngle: endAngle | |
}); | |
// If we need to show labels we need to add the label for this slice now | |
if(options.showLabel) { | |
// Position at the labelRadius distance from center and between start and end angle | |
var labelPosition = Chartist.polarToCartesian(center.x, center.y, labelRadius, startAngle + (endAngle - startAngle) / 2), | |
interpolatedValue = options.labelInterpolationFnc(this.data.labels ? (this.data.labels[i] + ' (' + dataArray[i] + ')'): dataArray[i], i); | |
if(interpolatedValue || interpolatedValue === 0) { | |
var labelElement = labelsGroup.elem('text', { | |
dx: labelPosition.x, | |
dy: labelPosition.y, | |
'text-anchor': determineAnchorPosition(center, labelPosition, options.labelDirection) | |
}, options.classNames.label).text('' + interpolatedValue); | |
// Fire off draw event | |
this.eventEmitter.emit('draw', { | |
type: 'label', | |
index: i, | |
group: labelsGroup, | |
element: labelElement, | |
text: '' + interpolatedValue, | |
x: labelPosition.x, | |
y: labelPosition.y | |
}); | |
} | |
} | |
// Set next startAngle to current endAngle. Use slight offset so there are no transparent hairline issues | |
// (except for last slice) | |
startAngle = endAngle; | |
} | |
this.eventEmitter.emit('created', { | |
chartRect: chartRect, | |
svg: this.svg, | |
options: options | |
}); | |
} | |
function Radial(query, data, options, responsiveOptions) { | |
Chartist.Radial.super.constructor.call(this, | |
query, | |
data, | |
defaultOptions, | |
Chartist.extend({}, defaultOptions, options), | |
responsiveOptions); | |
} | |
// Creating radial chart type in Chartist namespace | |
Chartist.Radial = Chartist.Base.extend({ | |
constructor: Radial, | |
createChart: createChart, | |
determineAnchorPosition: determineAnchorPosition | |
}); | |
}(window, document, Chartist)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="example-chart" class="ct-chart ct-golden-section"></div> | |
<script> | |
$(document).ready(function() { | |
new Chartist.Radial('#example-chart', {labels: ['Theoretical', 'Utilitarian', 'Individualistic', 'Aesthetic', 'Social', 'Traditional'], series: [68, 38, 29, 49, 46, 22]}, {maxValue: 70, chartPadding: 15, plugins: [Chartist.plugins.tooltip()]}); | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.ct-series-a .ct-slice-radial { | |
fill: #bb0000; | |
stroke: #bb0000 | |
} | |
.ct-series-b .ct-slice-radial { | |
fill: #d1bc3a; | |
stroke: #d1bc3a | |
} | |
.ct-series-c .ct-slice-radial { | |
fill: #000000; | |
stroke: #000000 | |
} | |
.ct-series-d .ct-slice-radial { | |
fill: #9b75bb; | |
stroke: #9b75bb | |
} | |
.ct-series-e .ct-slice-radial { | |
fill: #3ebba8; | |
stroke: #3ebba8 | |
} | |
.ct-series-f .ct-slice-radial { | |
fill: #aaaaaa; | |
stroke: #aaaaaa | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment