-
Notifications
You must be signed in to change notification settings - Fork 4
/
donut-chart.js
107 lines (96 loc) · 2.53 KB
/
donut-chart.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
function DonutChart(parent, spec) {
var __polar2xy = function(a, r) {
return {
x: Math.cos(a * 2 * Math.PI) * r,
y: -Math.sin(a * 2 * Math.PI) * r,
}
}
var __gen_arc_path = function(cx, cy, r, start, offset) {
var end = __polar2xy(start + offset, r)
start = __polar2xy(start, r)
return [
"M", cx + start.x, cy + start.y,
"A", r, r, 0, +(offset > .5), 0, cx + end.x, cy + end.y,
].join(" ")
}
var __gen_chart_item = function(out, c, r, prev, cur, i, stroke) {
out.push(["path", {
d: __gen_arc_path(c, c, r, prev, cur),
class: "chart-item-" + i,
fill: "transparent",
"stroke-width": stroke,
}])
}
var __gen_chart = function(chart) {
var prev = 0, out = []
// FIXME get radius and stroke-width from CSS
var c = chart.r, r = chart.r - chart.stroke / 2
for (var i in chart.items) {
cur = chart.items[i]
__gen_chart_item(out, c, r, prev, cur.value, i, chart.stroke)
prev += cur.value
}
if (prev < 1) {
__gen_chart_item(out, c, r, prev, 1 - prev, "bg", chart.stroke)
}
return out
}
var __create_tag_tree = function(elem) {
var root = document.createElementNS("http://www.w3.org/2000/svg", elem[0])
var attr = elem[1]
// Set attributes
for (var i in attr) {
var a = document.createAttribute(i)
a.value = attr[i]
root.setAttributeNode(a)
}
// Create children nodes
if (elem.length > 2) {
var children = elem[2]
for (var i in children) {
var c = __create_tag_tree(children[i])
root.appendChild(c)
}
}
return root
}
/* Transformation matrix (rotate and mirror) to correct orientation:
* \[
* \left[
* \begin{array}{ccc}
* 0 & -1 & 0 \\
* -1 & 0 & 0 \\
* 0 & 0 & 1
* \end{array}
* \right]
* \]
*/
var correct_orientation = "matrix(0 -1 -1 0 0 0)"
var __gen_code = function(spec) {
return __create_tag_tree(
["svg", {
transform: correct_orientation,
class: "chart-donut",
width: spec.r * 2,
height: spec.r * 2,
}, __gen_chart(spec)])
}
var __is_dict = function(v) {
return v && typeof v === "object" && !(v instanceof Array)
}
DonutChart.prototype.update = function(spec) {
// Merge the new spec
for (var i in spec) {
this.spec[i] = spec[i]
}
var code = __gen_code(this.spec)
// TODO can we switch the elements in place?
if (this.element != undefined) {
this.element.remove()
}
this.element = this.parent.appendChild(code)
}
this.parent = parent
this.spec = spec
this.update({})
}