Skip to content

Instantly share code, notes, and snippets.

@blackwatertepes
Created May 13, 2012 16:31
Show Gist options
  • Save blackwatertepes/2689200 to your computer and use it in GitHub Desktop.
Save blackwatertepes/2689200 to your computer and use it in GitHub Desktop.
A donut chart object to be used with the raphael.js library. Extended from the pie chart object.
/*
* g.Raphael 0.5 - Charting library, based on Raphaël
*
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*/
(function () {
function Piechart(paper, cx, cy, ir, or, values, opts) {
opts = opts || {};
var chartinst = this,
sectors = [],
covers = paper.set(),
chart = paper.set(),
series = paper.set(),
order = [],
len = values.length,
angle = 0,
total = 0,
others = 0,
cut = 9,
defcut = true;
function sector(cx, cy, ir, or, startAngle, endAngle, fill) {
var rad = Math.PI / 180,
ox1 = cx + or * Math.cos(-startAngle * rad),
ox2 = cx + or * Math.cos(-endAngle * rad),
ix1 = cx + ir * Math.cos(-startAngle * rad),
ix2 = cx + ir * Math.cos(-endAngle * rad),
xm = cx + or / 2 * Math.cos(-(startAngle + (endAngle - startAngle) / 2) * rad),
oy1 = cy + or * Math.sin(-startAngle * rad),
oy2 = cy + or * Math.sin(-endAngle * rad),
iy1 = cy + ir * Math.sin(-startAngle * rad),
iy2 = cy + ir * Math.sin(-endAngle * rad),
ym = cy + or / 2 * Math.sin(-(startAngle + (endAngle - startAngle) / 2) * rad),
res = [
"M", ix1, iy1,
"A", ir, ir, 0, +(Math.abs(endAngle - startAngle) > 180), 1, ix2, iy2,
"L", ox2, oy2,
"A", or, or, 0, +(Math.abs(endAngle - startAngle) > 180), 0, ox1, oy1,
"z"
];
res.middle = { x: xm, y: ym };
return res;
}
chart.covers = covers;
if (len == 1) {
//Currently a value of 1 will return a circle, and not a donut
series.push(paper.circle(cx, cy, or).attr({ fill: chartinst.colors[0], stroke: opts.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth }));
covers.push(paper.circle(cx, cy, or).attr(chartinst.shim));
total = values[0];
values[0] = { value: values[0], order: 0, valueOf: function () { return this.value; } };
series[0].middle = {x: cx, y: cy};
series[0].mangle = 180;
} else {
for (var i = 0; i < len; i++) {
total += values[i];
values[i] = { value: values[i], order: i, valueOf: function () { return this.value; } };
}
values.sort(function (a, b) {
return b.value - a.value;
});
for (i = 0; i < len; i++) {
if (defcut && values[i] * 360 / total <= 1.5) {
cut = i;
defcut = false;
}
if (i > cut) {
defcut = false;
values[cut].value += values[i];
values[cut].others = true;
others = values[cut].value;
}
}
len = Math.min(cut + 1, values.length);
others && values.splice(len) && (values[cut].others = true);
for (i = 0; i < len; i++) {
var mangle = angle - 360 * values[i] / total / 2;
if (!i) {
angle = 90 - mangle;
mangle = angle - 360 * values[i] / total / 2;
}
if (opts.init) {
var ipath = sector(cx, cy, 1, angle, angle - 360 * values[i] / total).join(",");
}
var path = sector(cx, cy, ir, or, angle, angle -= 360 * values[i] / total);
var p = paper.path(opts.init ? ipath : path).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#666", opacity: opts.opacity[i] || 1, stroke: opts.stroke || "#fff", "stroke-width": (opts.strokewidth == null ? 1 : opts.strokewidth), "stroke-linejoin": "round" });
p.value = values[i];
p.middle = path.middle;
p.mangle = mangle;
sectors.push(p);
series.push(p);
opts.init && p.animate({ path: path.join(",") }, (+opts.init - 1) || 1000, ">");
}
for (i = 0; i < len; i++) {
p = paper.path(sectors[i].attr("path")).attr(chartinst.shim);
opts.href && opts.href[i] && p.attr({ href: opts.href[i] });
p.attr = function () {};
covers.push(p);
series.push(p);
}
}
chart.hover = function (fin, fout) {
fout = fout || function () {};
var that = this;
for (var i = 0; i < len; i++) {
(function (sector, cover, j) {
var o = {
sector: sector,
cover: cover,
cx: cx,
cy: cy,
mx: sector.middle.x,
my: sector.middle.y,
mangle: sector.mangle,
ir: ir,
or: or,
value: values[j],
total: total,
label: that.labels && that.labels[j]
};
cover.mouseover(function () {
fin.call(o);
}).mouseout(function () {
fout.call(o);
});
})(series[i], covers[i], i);
}
return this;
};
// x: where label could be put
// y: where label could be put
// value: value to show
// total: total number to count %
chart.each = function (f) {
var that = this;
for (var i = 0; i < len; i++) {
(function (sector, cover, j) {
var o = {
sector: sector,
cover: cover,
cx: cx,
cy: cy,
x: sector.middle.x,
y: sector.middle.y,
mangle: sector.mangle,
ir: ir,
or: or,
value: values[j],
total: total,
label: that.labels && that.labels[j]
};
f.call(o);
})(series[i], covers[i], i);
}
return this;
};
chart.click = function (f) {
var that = this;
for (var i = 0; i < len; i++) {
(function (sector, cover, j) {
var o = {
sector: sector,
cover: cover,
cx: cx,
cy: cy,
mx: sector.middle.x,
my: sector.middle.y,
mangle: sector.mangle,
ir: ir,
or: or,
value: values[j],
total: total,
label: that.labels && that.labels[j]
};
cover.click(function () { f.call(o); });
})(series[i], covers[i], i);
}
return this;
};
chart.inject = function (element) {
element.insertBefore(covers[0]);
};
var legend = function (labels, otherslabel, mark, dir) {
var x = cx + or + or / 5,
y = cy,
h = y + 10;
labels = labels || [];
dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east";
mark = paper[mark && mark.toLowerCase()] || "circle";
chart.labels = paper.set();
for (var i = 0; i < len; i++) {
var clr = series[i].attr("fill"),
j = values[i].order,
txt;
values[i].others && (labels[j] = otherslabel || "Others");
labels[j] = chartinst.labelise(labels[j], values[i], total);
chart.labels.push(paper.set());
chart.labels[i].push(paper[mark](x + 5, h, 5).attr({ fill: clr, stroke: "none" }));
chart.labels[i].push(txt = paper.text(x + 20, h, labels[j] || values[j]).attr(chartinst.txtattr).attr({ fill: opts.legendcolor || "#000", "text-anchor": "start"}));
covers[i].label = chart.labels[i];
h += txt.getBBox().height * 1.2;
}
var bb = chart.labels.getBBox(),
tr = {
east: [0, -bb.height / 2],
west: [-bb.width - 2 * or - 20, -bb.height / 2],
north: [-or - bb.width / 2, -or - bb.height - 10],
south: [-or - bb.width / 2, or + 10]
}[dir];
chart.labels.translate.apply(chart.labels, tr);
chart.push(chart.labels);
};
if (opts.legend) {
legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
}
chart.push(series, covers);
chart.series = series;
chart.covers = covers;
return chart;
};
//inheritance
var F = function() {};
F.prototype = Raphael.g;
Piechart.prototype = new F;
//public
Raphael.fn.piechart = function(cx, cy, ir, or, values, opts) {
return new Piechart(this, cx, cy, ir, or, values, opts);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment