I’ve been working on an open source, AngularJS-based webapp for building charts called axisJS.
After a significant amount of development over the past few months, I’m pleased to announce the release of version 1.0.0, which is pretty stable and functional at this point. Although we’re already using axisJS in production with a fair degree of regularity, version 1.0.0 is the most stable and feature rich yet, and is built to make it really easy for developers to extend and customise.
What kind of charts can you make with axisJS? You can make line charts…
(function(){var configJSON = JSON.parse('{"data":{"x":"","y":"","y2":"","columns":[["llamas","30","200","100","400","150","250"],["ducks","50","20","10","40","15","25"]],"axes":{},"groups":{},"type":"","types":{"data1":"line","data2":"line","llamas":"line","ducks":"line"},"colors":{"data1":"#78b8df","data2":"#afcbce","llamas":"#78b8df","ducks":"#44cc22"}},"grid":{"x":{"show":false},"y":{"show":false}},"axis":{"x":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.x.type !== \'category\') { return config.axis.x.prefix + d.toFixed(config.axis.x.accuracy).toString() + config.axis.x.suffix; } else { return d; } }"}},"y":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y.type !== \'category\') { return config.axis.y.prefix + d.toFixed(config.axis.y.accuracy).toString() + config.axis.y.suffix; } else { return d; } }"}},"y2":{"show":false,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y2.type !== \'category\') { return config.axis.y2.prefix + d.toFixed(config.axis.y2.accuracy).toString() + config.axis.y2.suffix; } else { return d; } }"}}},"point":{"show":false},"legend":{"position":"bottom","show":true},"color":{"pattern":["#78B8DF","#AFCBCE","#23393D","#87AF9C","#0EBF00","#ED1B24","#808080","#ED1B24","#022397","#FDBB30","#722889","#6AB023","#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"]},"groups":{},"chartTitle":"","chartCredit":"","chartSource":"","chartWidth":1000,"chartGlobalType":"series","chartAccuracy":1,"cms":false,"pie":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"donut":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"gauge":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"background":false,"backgroundColor":"white","bindto":"#chart-5316"}');var fixJson = function(obj){for(var i in obj)obj.hasOwnProperty(i)&&("string"==typeof obj[i]&&obj[i].match(/^function/)?obj[i]=eval("("+obj[i]+")"):"object"==typeof obj[i]&&fixJson(obj[i]));return obj};var config = fixJson(configJSON);c3.generate(config);})();
(function(){var configJSON = JSON.parse('{"data":{"x":"","y":"","y2":"","columns":[["llamas","30","200","100","400","150","250"],["ducks","50","20","10","40","15","25"]],"axes":{},"groups":{},"type":"","types":{"data1":"line","data2":"line","llamas":"bar","ducks":"bar"},"colors":{"data1":"#78b8df","data2":"#afcbce","llamas":"#78b8df","ducks":"#44cc22"}},"grid":{"x":{"show":false},"y":{"show":false}},"axis":{"x":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.x.type !== \'category\') { return config.axis.x.prefix + d.toFixed(config.axis.x.accuracy).toString() + config.axis.x.suffix; } else { return d; } }"}},"y":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y.type !== \'category\') { return config.axis.y.prefix + d.toFixed(config.axis.y.accuracy).toString() + config.axis.y.suffix; } else { return d; } }"}},"y2":{"show":false,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y2.type !== \'category\') { return config.axis.y2.prefix + d.toFixed(config.axis.y2.accuracy).toString() + config.axis.y2.suffix; } else { return d; } }"}}},"point":{"show":false},"legend":{"position":"bottom","show":true},"color":{"pattern":["#78B8DF","#AFCBCE","#23393D","#87AF9C","#0EBF00","#ED1B24","#808080","#ED1B24","#022397","#FDBB30","#722889","#6AB023","#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"]},"groups":{},"chartTitle":"","chartCredit":"","chartSource":"","chartWidth":1000,"chartGlobalType":"series","chartAccuracy":1,"cms":false,"pie":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"donut":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"gauge":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"background":false,"backgroundColor":"white","bindto":"#chart-3981"}');var fixJson = function(obj){for(var i in obj)obj.hasOwnProperty(i)&&("string"==typeof obj[i]&&obj[i].match(/^function/)?obj[i]=eval("("+obj[i]+")"):"object"==typeof obj[i]&&fixJson(obj[i]));return obj};var config = fixJson(configJSON);c3.generate(config);})();
(function(){var configJSON = JSON.parse('{"data":{"x":"","y":"","y2":"","columns":[["llamas","30","200","100","400","150","250"],["ducks","50","20","10","40","15","25"]],"axes":{},"groups":{},"type":"","types":{"data1":"line","data2":"line","llamas":"area","ducks":"bar"},"colors":{"data1":"#78b8df","data2":"#afcbce","llamas":"#78b8df","ducks":"#44cc22"}},"grid":{"x":{"show":false},"y":{"show":false}},"axis":{"x":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.x.type !== \'category\') { return config.axis.x.prefix + d.toFixed(config.axis.x.accuracy).toString() + config.axis.x.suffix; } else { return d; } }"}},"y":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y.type !== \'category\') { return config.axis.y.prefix + d.toFixed(config.axis.y.accuracy).toString() + config.axis.y.suffix; } else { return d; } }"}},"y2":{"show":false,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y2.type !== \'category\') { return config.axis.y2.prefix + d.toFixed(config.axis.y2.accuracy).toString() + config.axis.y2.suffix; } else { return d; } }"}}},"point":{"show":false},"legend":{"position":"bottom","show":true},"color":{"pattern":["#78B8DF","#AFCBCE","#23393D","#87AF9C","#0EBF00","#ED1B24","#808080","#ED1B24","#022397","#FDBB30","#722889","#6AB023","#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"]},"groups":{},"chartTitle":"","chartCredit":"","chartSource":"","chartWidth":1000,"chartGlobalType":"series","chartAccuracy":1,"cms":false,"pie":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"donut":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"gauge":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"background":false,"backgroundColor":"white","bindto":"#chart-2358"}');var fixJson = function(obj){for(var i in obj)obj.hasOwnProperty(i)&&("string"==typeof obj[i]&&obj[i].match(/^function/)?obj[i]=eval("("+obj[i]+")"):"object"==typeof obj[i]&&fixJson(obj[i]));return obj};var config = fixJson(configJSON);c3.generate(config);})();
(function(){var configJSON = JSON.parse('{"data":{"x":"","y":"","y2":"","columns":[["llamas","30","200","100","400","150","250"],["ducks","50","20","10","40","15","25"]],"axes":{},"groups":{},"type":"","types":{"data1":"donut","data2":"donut","llamas":"donut","ducks":"donut"},"colors":{"data1":"#78b8df","data2":"#afcbce","llamas":"#78b8df","ducks":"#44cc22"}},"grid":{"x":{"show":false},"y":{"show":false}},"axis":{"x":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.x.type !== \'category\') { return config.axis.x.prefix + d.toFixed(config.axis.x.accuracy).toString() + config.axis.x.suffix; } else { return d; } }"}},"y":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y.type !== \'category\') { return config.axis.y.prefix + d.toFixed(config.axis.y.accuracy).toString() + config.axis.y.suffix; } else { return d; } }"}},"y2":{"show":false,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y2.type !== \'category\') { return config.axis.y2.prefix + d.toFixed(config.axis.y2.accuracy).toString() + config.axis.y2.suffix; } else { return d; } }"}}},"point":{"show":false},"legend":{"position":"bottom","show":true},"color":{"pattern":["#78B8DF","#AFCBCE","#23393D","#87AF9C","#0EBF00","#ED1B24","#808080","#ED1B24","#022397","#FDBB30","#722889","#6AB023","#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"]},"groups":{},"chartTitle":"","chartCredit":"","chartSource":"","chartWidth":1000,"chartGlobalType":"donut","chartAccuracy":1,"cms":false,"pie":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"donut":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"gauge":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"background":false,"backgroundColor":"white","bindto":"#chart-7890"}');var fixJson = function(obj){for(var i in obj)obj.hasOwnProperty(i)&&("string"==typeof obj[i]&&obj[i].match(/^function/)?obj[i]=eval("("+obj[i]+")"):"object"==typeof obj[i]&&fixJson(obj[i]));return obj};var config = fixJson(configJSON);c3.generate(config);})();
(function(){var configJSON = JSON.parse('{"data":{"x":"","y":"","y2":"","columns":[["llamas","55"]],"axes":{},"groups":{},"type":"","types":{"data1":"gauge","data2":"gauge","llamas":"gauge","ducks":"gauge"},"colors":{"data1":"#78b8df","data2":"#afcbce","llamas":"#77e088","ducks":"#44cc22"}},"grid":{"x":{"show":false},"y":{"show":false}},"axis":{"x":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.x.type !== \'category\') { return config.axis.x.prefix + d.toFixed(config.axis.x.accuracy).toString() + config.axis.x.suffix; } else { return d; } }"}},"y":{"show":true,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y.type !== \'category\') { return config.axis.y.prefix + d.toFixed(config.axis.y.accuracy).toString() + config.axis.y.suffix; } else { return d; } }"}},"y2":{"show":false,"accuracy":0,"prefix":"","suffix":"","tick":{"format":"function (d) { if (config.chartGlobalType === \'series\' && config.axis.y2.type !== \'category\') { return config.axis.y2.prefix + d.toFixed(config.axis.y2.accuracy).toString() + config.axis.y2.suffix; } else { return d; } }"}}},"point":{"show":false},"legend":{"position":"bottom","show":true},"color":{"pattern":["#78B8DF","#AFCBCE","#23393D","#87AF9C","#0EBF00","#ED1B24","#808080","#ED1B24","#022397","#FDBB30","#722889","#6AB023","#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"]},"groups":{},"chartTitle":"","chartCredit":"","chartSource":"","chartWidth":1000,"chartGlobalType":"gauge","chartAccuracy":1,"cms":false,"pie":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"donut":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"gauge":{"label":{"format":"function (val, percentage) { return (percentage * 100).toFixed(config.chartAccuracy) + \'%\'; }"}},"background":false,"backgroundColor":"white","bindto":"#chart-3166"}');var fixJson = function(obj){for(var i in obj)obj.hasOwnProperty(i)&&("string"==typeof obj[i]&&obj[i].match(/^function/)?obj[i]=eval("("+obj[i]+")"):"object"==typeof obj[i]&&fixJson(obj[i]));return obj};var config = fixJson(configJSON);c3.generate(config);})();
(All of the above took me about a minute in total to generate.)
You can add titles, change around the axes, specify colours and more — all without writing a single line of code. axisJS charts can currently be saved as PNG, SVG or PDF, and exported to WordPress (See our Axis WordPress plugin) or any CMS via embed code. Plugins for other CMSes are on the way, and everything is incredibly modular and extensible — writing a plugin for a new CMS or output type is really straight-forward and easy (I’m currently working on developer documentation — see the wiki).
We’ve also architected this so it’s really straight-forward for newsrooms to style axisJS charts to reflect their stylesheets. Everything is configured via a YAML config file, with additional styling in title-specific stylesheets. Because static graphics have been given quite a lot of attention, axisJS charts can even be used in print production — indeed, we’re planning on replacing the daily hand-generated Adobe Illustrator graphs seen in The Times _Business section with _axisJS charts, in turn saving the graphics team an enormous amount of time.
What’s next, then? In addition to improving the UI and documentation, we’re working on AxisMaker, an interface that will allow anyone to easily create and host interactive axisJS charts on GitHub for free, using GitHub Pages. We’re also making support for print output even better and contributing back to C3.js, the underlying D3-based library that renders the charts.
Watch this space for more information about AxisMaker. In the meantime, try out axisJS online here!
-- Ændrew Rininsland is the lead developer on axisJS and Doctop. Follow @aendrew on Twitter and GitHub. He also blogs at aendrew.tumblr.com.