1
|
(function(){
|
2
|
"use strict";
|
3
|
|
4
|
var root = this,
|
5
|
Chart = root.Chart,
|
6
|
//Cache a local reference to Chart.helpers
|
7
|
helpers = Chart.helpers;
|
8
|
|
9
|
var defaultConfig = {
|
10
|
//Boolean - Show a backdrop to the scale label
|
11
|
scaleShowLabelBackdrop : true,
|
12
|
|
13
|
//String - The colour of the label backdrop
|
14
|
scaleBackdropColor : "rgba(255,255,255,0.75)",
|
15
|
|
16
|
// Boolean - Whether the scale should begin at zero
|
17
|
scaleBeginAtZero : true,
|
18
|
|
19
|
//Number - The backdrop padding above & below the label in pixels
|
20
|
scaleBackdropPaddingY : 2,
|
21
|
|
22
|
//Number - The backdrop padding to the side of the label in pixels
|
23
|
scaleBackdropPaddingX : 2,
|
24
|
|
25
|
//Boolean - Show line for each value in the scale
|
26
|
scaleShowLine : true,
|
27
|
|
28
|
//Boolean - Stroke a line around each segment in the chart
|
29
|
segmentShowStroke : true,
|
30
|
|
31
|
//String - The colour of the stroke on each segement.
|
32
|
segmentStrokeColor : "#fff",
|
33
|
|
34
|
//Number - The width of the stroke value in pixels
|
35
|
segmentStrokeWidth : 2,
|
36
|
|
37
|
//Number - Amount of animation steps
|
38
|
animationSteps : 100,
|
39
|
|
40
|
//String - Animation easing effect.
|
41
|
animationEasing : "easeOutBounce",
|
42
|
|
43
|
//Boolean - Whether to animate the rotation of the chart
|
44
|
animateRotate : true,
|
45
|
|
46
|
//Boolean - Whether to animate scaling the chart from the centre
|
47
|
animateScale : false,
|
48
|
|
49
|
//String - A legend template
|
50
|
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
|
51
|
};
|
52
|
|
53
|
|
54
|
Chart.Type.extend({
|
55
|
//Passing in a name registers this chart in the Chart namespace
|
56
|
name: "PolarArea",
|
57
|
//Providing a defaults will also register the deafults in the chart namespace
|
58
|
defaults : defaultConfig,
|
59
|
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
60
|
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
61
|
initialize: function(data){
|
62
|
this.segments = [];
|
63
|
//Declare segment class as a chart instance specific class, so it can share props for this instance
|
64
|
this.SegmentArc = Chart.Arc.extend({
|
65
|
showStroke : this.options.segmentShowStroke,
|
66
|
strokeWidth : this.options.segmentStrokeWidth,
|
67
|
strokeColor : this.options.segmentStrokeColor,
|
68
|
ctx : this.chart.ctx,
|
69
|
innerRadius : 0,
|
70
|
x : this.chart.width/2,
|
71
|
y : this.chart.height/2
|
72
|
});
|
73
|
this.scale = new Chart.RadialScale({
|
74
|
display: this.options.showScale,
|
75
|
fontStyle: this.options.scaleFontStyle,
|
76
|
fontSize: this.options.scaleFontSize,
|
77
|
fontFamily: this.options.scaleFontFamily,
|
78
|
fontColor: this.options.scaleFontColor,
|
79
|
showLabels: this.options.scaleShowLabels,
|
80
|
showLabelBackdrop: this.options.scaleShowLabelBackdrop,
|
81
|
backdropColor: this.options.scaleBackdropColor,
|
82
|
backdropPaddingY : this.options.scaleBackdropPaddingY,
|
83
|
backdropPaddingX: this.options.scaleBackdropPaddingX,
|
84
|
lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
|
85
|
lineColor: this.options.scaleLineColor,
|
86
|
lineArc: true,
|
87
|
width: this.chart.width,
|
88
|
height: this.chart.height,
|
89
|
xCenter: this.chart.width/2,
|
90
|
yCenter: this.chart.height/2,
|
91
|
ctx : this.chart.ctx,
|
92
|
templateString: this.options.scaleLabel,
|
93
|
valuesCount: data.length
|
94
|
});
|
95
|
|
96
|
this.updateScaleRange(data);
|
97
|
|
98
|
this.scale.update();
|
99
|
|
100
|
helpers.each(data,function(segment,index){
|
101
|
this.addData(segment,index,true);
|
102
|
},this);
|
103
|
|
104
|
//Set up tooltip events on the chart
|
105
|
if (this.options.showTooltips){
|
106
|
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
107
|
var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
|
108
|
helpers.each(this.segments,function(segment){
|
109
|
segment.restore(["fillColor"]);
|
110
|
});
|
111
|
helpers.each(activeSegments,function(activeSegment){
|
112
|
activeSegment.fillColor = activeSegment.highlightColor;
|
113
|
});
|
114
|
this.showTooltip(activeSegments);
|
115
|
});
|
116
|
}
|
117
|
|
118
|
this.render();
|
119
|
},
|
120
|
getSegmentsAtEvent : function(e){
|
121
|
var segmentsArray = [];
|
122
|
|
123
|
var location = helpers.getRelativePosition(e);
|
124
|
|
125
|
helpers.each(this.segments,function(segment){
|
126
|
if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
|
127
|
},this);
|
128
|
return segmentsArray;
|
129
|
},
|
130
|
addData : function(segment, atIndex, silent){
|
131
|
var index = atIndex || this.segments.length;
|
132
|
|
133
|
this.segments.splice(index, 0, new this.SegmentArc({
|
134
|
fillColor: segment.color,
|
135
|
highlightColor: segment.highlight || segment.color,
|
136
|
label: segment.label,
|
137
|
value: segment.value,
|
138
|
outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value),
|
139
|
circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(),
|
140
|
startAngle: Math.PI * 1.5
|
141
|
}));
|
142
|
if (!silent){
|
143
|
this.reflow();
|
144
|
this.update();
|
145
|
}
|
146
|
},
|
147
|
removeData: function(atIndex){
|
148
|
var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
|
149
|
this.segments.splice(indexToDelete, 1);
|
150
|
this.reflow();
|
151
|
this.update();
|
152
|
},
|
153
|
calculateTotal: function(data){
|
154
|
this.total = 0;
|
155
|
helpers.each(data,function(segment){
|
156
|
this.total += segment.value;
|
157
|
},this);
|
158
|
this.scale.valuesCount = this.segments.length;
|
159
|
},
|
160
|
updateScaleRange: function(datapoints){
|
161
|
var valuesArray = [];
|
162
|
helpers.each(datapoints,function(segment){
|
163
|
valuesArray.push(segment.value);
|
164
|
});
|
165
|
|
166
|
var scaleSizes = (this.options.scaleOverride) ?
|
167
|
{
|
168
|
steps: this.options.scaleSteps,
|
169
|
stepValue: this.options.scaleStepWidth,
|
170
|
min: this.options.scaleStartValue,
|
171
|
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
172
|
} :
|
173
|
helpers.calculateScaleRange(
|
174
|
valuesArray,
|
175
|
helpers.min([this.chart.width, this.chart.height])/2,
|
176
|
this.options.scaleFontSize,
|
177
|
this.options.scaleBeginAtZero,
|
178
|
this.options.scaleIntegersOnly
|
179
|
);
|
180
|
|
181
|
helpers.extend(
|
182
|
this.scale,
|
183
|
scaleSizes,
|
184
|
{
|
185
|
size: helpers.min([this.chart.width, this.chart.height]),
|
186
|
xCenter: this.chart.width/2,
|
187
|
yCenter: this.chart.height/2
|
188
|
}
|
189
|
);
|
190
|
|
191
|
},
|
192
|
update : function(){
|
193
|
this.calculateTotal(this.segments);
|
194
|
|
195
|
helpers.each(this.segments,function(segment){
|
196
|
segment.save();
|
197
|
});
|
198
|
|
199
|
this.reflow();
|
200
|
this.render();
|
201
|
},
|
202
|
reflow : function(){
|
203
|
helpers.extend(this.SegmentArc.prototype,{
|
204
|
x : this.chart.width/2,
|
205
|
y : this.chart.height/2
|
206
|
});
|
207
|
this.updateScaleRange(this.segments);
|
208
|
this.scale.update();
|
209
|
|
210
|
helpers.extend(this.scale,{
|
211
|
xCenter: this.chart.width/2,
|
212
|
yCenter: this.chart.height/2
|
213
|
});
|
214
|
|
215
|
helpers.each(this.segments, function(segment){
|
216
|
segment.update({
|
217
|
outerRadius : this.scale.calculateCenterOffset(segment.value)
|
218
|
});
|
219
|
}, this);
|
220
|
|
221
|
},
|
222
|
draw : function(ease){
|
223
|
var easingDecimal = ease || 1;
|
224
|
//Clear & draw the canvas
|
225
|
this.clear();
|
226
|
helpers.each(this.segments,function(segment, index){
|
227
|
segment.transition({
|
228
|
circumference : this.scale.getCircumference(),
|
229
|
outerRadius : this.scale.calculateCenterOffset(segment.value)
|
230
|
},easingDecimal);
|
231
|
|
232
|
segment.endAngle = segment.startAngle + segment.circumference;
|
233
|
|
234
|
// If we've removed the first segment we need to set the first one to
|
235
|
// start at the top.
|
236
|
if (index === 0){
|
237
|
segment.startAngle = Math.PI * 1.5;
|
238
|
}
|
239
|
|
240
|
//Check to see if it's the last segment, if not get the next and update the start angle
|
241
|
if (index < this.segments.length - 1){
|
242
|
this.segments[index+1].startAngle = segment.endAngle;
|
243
|
}
|
244
|
segment.draw();
|
245
|
}, this);
|
246
|
this.scale.draw();
|
247
|
}
|
248
|
});
|
249
|
|
250
|
}).call(this);
|