easy_pie_chart 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,351 @@
1
+ /**!
2
+ * easyPieChart
3
+ * Lightweight plugin to render simple, animated and retina optimized pie charts
4
+ *
5
+ * @license
6
+ * @author Robert Fleischmann <rendro87@gmail.com> (http://robert-fleischmann.de)
7
+ * @version 2.1.6
8
+ **/
9
+
10
+ (function(root, factory) {
11
+ if(typeof exports === 'object') {
12
+ module.exports = factory();
13
+ }
14
+ else if(typeof define === 'function' && define.amd) {
15
+ define([], factory);
16
+ }
17
+ else {
18
+ root['EasyPieChart'] = factory();
19
+ }
20
+ }(this, function() {
21
+
22
+ /**
23
+ * Renderer to render the chart on a canvas object
24
+ * @param {DOMElement} el DOM element to host the canvas (root of the plugin)
25
+ * @param {object} options options object of the plugin
26
+ */
27
+ var CanvasRenderer = function(el, options) {
28
+ var cachedBackground;
29
+ var canvas = document.createElement('canvas');
30
+
31
+ el.appendChild(canvas);
32
+
33
+ if (typeof(G_vmlCanvasManager) !== 'undefined') {
34
+ G_vmlCanvasManager.initElement(canvas);
35
+ }
36
+
37
+ var ctx = canvas.getContext('2d');
38
+
39
+ canvas.width = canvas.height = options.size;
40
+
41
+ // canvas on retina devices
42
+ var scaleBy = 1;
43
+ if (window.devicePixelRatio > 1) {
44
+ scaleBy = window.devicePixelRatio;
45
+ canvas.style.width = canvas.style.height = [options.size, 'px'].join('');
46
+ canvas.width = canvas.height = options.size * scaleBy;
47
+ ctx.scale(scaleBy, scaleBy);
48
+ }
49
+
50
+ // move 0,0 coordinates to the center
51
+ ctx.translate(options.size / 2, options.size / 2);
52
+
53
+ // rotate canvas -90deg
54
+ ctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI);
55
+
56
+ var radius = (options.size - options.lineWidth) / 2;
57
+ if (options.scaleColor && options.scaleLength) {
58
+ radius -= options.scaleLength + 2; // 2 is the distance between scale and bar
59
+ }
60
+
61
+ // IE polyfill for Date
62
+ Date.now = Date.now || function() {
63
+ return +(new Date());
64
+ };
65
+
66
+ /**
67
+ * Draw a circle around the center of the canvas
68
+ * @param {strong} color Valid CSS color string
69
+ * @param {number} lineWidth Width of the line in px
70
+ * @param {number} percent Percentage to draw (float between -1 and 1)
71
+ */
72
+ var drawCircle = function(color, lineWidth, percent) {
73
+ percent = Math.min(Math.max(-1, percent || 0), 1);
74
+ var isNegative = percent <= 0 ? true : false;
75
+
76
+ ctx.beginPath();
77
+ ctx.arc(0, 0, radius, 0, Math.PI * 2 * percent, isNegative);
78
+
79
+ ctx.strokeStyle = color;
80
+ ctx.lineWidth = lineWidth;
81
+
82
+ ctx.stroke();
83
+ };
84
+
85
+ /**
86
+ * Draw the scale of the chart
87
+ */
88
+ var drawScale = function() {
89
+ var offset;
90
+ var length;
91
+
92
+ ctx.lineWidth = 1;
93
+ ctx.fillStyle = options.scaleColor;
94
+
95
+ ctx.save();
96
+ for (var i = 24; i > 0; --i) {
97
+ if (i % 6 === 0) {
98
+ length = options.scaleLength;
99
+ offset = 0;
100
+ } else {
101
+ length = options.scaleLength * 0.6;
102
+ offset = options.scaleLength - length;
103
+ }
104
+ ctx.fillRect(-options.size/2 + offset, 0, length, 1);
105
+ ctx.rotate(Math.PI / 12);
106
+ }
107
+ ctx.restore();
108
+ };
109
+
110
+ /**
111
+ * Request animation frame wrapper with polyfill
112
+ * @return {function} Request animation frame method or timeout fallback
113
+ */
114
+ var reqAnimationFrame = (function() {
115
+ return window.requestAnimationFrame ||
116
+ window.webkitRequestAnimationFrame ||
117
+ window.mozRequestAnimationFrame ||
118
+ function(callback) {
119
+ window.setTimeout(callback, 1000 / 60);
120
+ };
121
+ }());
122
+
123
+ /**
124
+ * Draw the background of the plugin including the scale and the track
125
+ */
126
+ var drawBackground = function() {
127
+ if(options.scaleColor) drawScale();
128
+ if(options.trackColor) drawCircle(options.trackColor, options.trackWidth || options.lineWidth, 1);
129
+ };
130
+
131
+ /**
132
+ * Canvas accessor
133
+ */
134
+ this.getCanvas = function() {
135
+ return canvas;
136
+ };
137
+
138
+ /**
139
+ * Canvas 2D context 'ctx' accessor
140
+ */
141
+ this.getCtx = function() {
142
+ return ctx;
143
+ };
144
+
145
+ /**
146
+ * Clear the complete canvas
147
+ */
148
+ this.clear = function() {
149
+ ctx.clearRect(options.size / -2, options.size / -2, options.size, options.size);
150
+ };
151
+
152
+ /**
153
+ * Draw the complete chart
154
+ * @param {number} percent Percent shown by the chart between -100 and 100
155
+ */
156
+ this.draw = function(percent) {
157
+ // do we need to render a background
158
+ if (!!options.scaleColor || !!options.trackColor) {
159
+ // getImageData and putImageData are supported
160
+ if (ctx.getImageData && ctx.putImageData) {
161
+ if (!cachedBackground) {
162
+ drawBackground();
163
+ cachedBackground = ctx.getImageData(0, 0, options.size * scaleBy, options.size * scaleBy);
164
+ } else {
165
+ ctx.putImageData(cachedBackground, 0, 0);
166
+ }
167
+ } else {
168
+ this.clear();
169
+ drawBackground();
170
+ }
171
+ } else {
172
+ this.clear();
173
+ }
174
+
175
+ ctx.lineCap = options.lineCap;
176
+
177
+ // if barcolor is a function execute it and pass the percent as a value
178
+ var color;
179
+ if (typeof(options.barColor) === 'function') {
180
+ color = options.barColor(percent);
181
+ } else {
182
+ color = options.barColor;
183
+ }
184
+
185
+ // draw bar
186
+ drawCircle(color, options.lineWidth, percent / 100);
187
+ }.bind(this);
188
+
189
+ /**
190
+ * Animate from some percent to some other percentage
191
+ * @param {number} from Starting percentage
192
+ * @param {number} to Final percentage
193
+ */
194
+ this.animate = function(from, to) {
195
+ var startTime = Date.now();
196
+ options.onStart(from, to);
197
+ var animation = function() {
198
+ var process = Math.min(Date.now() - startTime, options.animate.duration);
199
+ var currentValue = options.easing(this, process, from, to - from, options.animate.duration);
200
+ this.draw(currentValue);
201
+ options.onStep(from, to, currentValue);
202
+ if (process >= options.animate.duration) {
203
+ options.onStop(from, to);
204
+ } else {
205
+ reqAnimationFrame(animation);
206
+ }
207
+ }.bind(this);
208
+
209
+ reqAnimationFrame(animation);
210
+ }.bind(this);
211
+ };
212
+
213
+ var EasyPieChart = function(el, opts) {
214
+ var defaultOptions = {
215
+ barColor: '#ef1e25',
216
+ trackColor: '#f9f9f9',
217
+ scaleColor: '#dfe0e0',
218
+ scaleLength: 5,
219
+ lineCap: 'round',
220
+ lineWidth: 3,
221
+ trackWidth: undefined,
222
+ size: 110,
223
+ rotate: 0,
224
+ animate: {
225
+ duration: 1000,
226
+ enabled: true
227
+ },
228
+ easing: function (x, t, b, c, d) { // more can be found here: http://gsgd.co.uk/sandbox/jquery/easing/
229
+ t = t / (d/2);
230
+ if (t < 1) {
231
+ return c / 2 * t * t + b;
232
+ }
233
+ return -c/2 * ((--t)*(t-2) - 1) + b;
234
+ },
235
+ onStart: function(from, to) {
236
+ return;
237
+ },
238
+ onStep: function(from, to, currentValue) {
239
+ return;
240
+ },
241
+ onStop: function(from, to) {
242
+ return;
243
+ }
244
+ };
245
+
246
+ // detect present renderer
247
+ if (typeof(CanvasRenderer) !== 'undefined') {
248
+ defaultOptions.renderer = CanvasRenderer;
249
+ } else if (typeof(SVGRenderer) !== 'undefined') {
250
+ defaultOptions.renderer = SVGRenderer;
251
+ } else {
252
+ throw new Error('Please load either the SVG- or the CanvasRenderer');
253
+ }
254
+
255
+ var options = {};
256
+ var currentValue = 0;
257
+
258
+ /**
259
+ * Initialize the plugin by creating the options object and initialize rendering
260
+ */
261
+ var init = function() {
262
+ this.el = el;
263
+ this.options = options;
264
+
265
+ // merge user options into default options
266
+ for (var i in defaultOptions) {
267
+ if (defaultOptions.hasOwnProperty(i)) {
268
+ options[i] = opts && typeof(opts[i]) !== 'undefined' ? opts[i] : defaultOptions[i];
269
+ if (typeof(options[i]) === 'function') {
270
+ options[i] = options[i].bind(this);
271
+ }
272
+ }
273
+ }
274
+
275
+ // check for jQuery easing
276
+ if (typeof(options.easing) === 'string' && typeof(jQuery) !== 'undefined' && jQuery.isFunction(jQuery.easing[options.easing])) {
277
+ options.easing = jQuery.easing[options.easing];
278
+ } else {
279
+ options.easing = defaultOptions.easing;
280
+ }
281
+
282
+ // process earlier animate option to avoid bc breaks
283
+ if (typeof(options.animate) === 'number') {
284
+ options.animate = {
285
+ duration: options.animate,
286
+ enabled: true
287
+ };
288
+ }
289
+
290
+ if (typeof(options.animate) === 'boolean' && !options.animate) {
291
+ options.animate = {
292
+ duration: 1000,
293
+ enabled: options.animate
294
+ };
295
+ }
296
+
297
+ // create renderer
298
+ this.renderer = new options.renderer(el, options);
299
+
300
+ // initial draw
301
+ this.renderer.draw(currentValue);
302
+
303
+ // initial update
304
+ if (el.dataset && el.dataset.percent) {
305
+ this.update(parseFloat(el.dataset.percent));
306
+ } else if (el.getAttribute && el.getAttribute('data-percent')) {
307
+ this.update(parseFloat(el.getAttribute('data-percent')));
308
+ }
309
+ }.bind(this);
310
+
311
+ /**
312
+ * Update the value of the chart
313
+ * @param {number} newValue Number between 0 and 100
314
+ * @return {object} Instance of the plugin for method chaining
315
+ */
316
+ this.update = function(newValue) {
317
+ newValue = parseFloat(newValue);
318
+ if (options.animate.enabled) {
319
+ this.renderer.animate(currentValue, newValue);
320
+ } else {
321
+ this.renderer.draw(newValue);
322
+ }
323
+ currentValue = newValue;
324
+ return this;
325
+ }.bind(this);
326
+
327
+ /**
328
+ * Disable animation
329
+ * @return {object} Instance of the plugin for method chaining
330
+ */
331
+ this.disableAnimation = function() {
332
+ options.animate.enabled = false;
333
+ return this;
334
+ };
335
+
336
+ /**
337
+ * Enable animation
338
+ * @return {object} Instance of the plugin for method chaining
339
+ */
340
+ this.enableAnimation = function() {
341
+ options.animate.enabled = true;
342
+ return this;
343
+ };
344
+
345
+ init();
346
+ };
347
+
348
+
349
+ return EasyPieChart;
350
+
351
+ }));
@@ -0,0 +1,316 @@
1
+ ###*
2
+ !
3
+ easyPieChart
4
+ Lightweight plugin to render simple, animated and retina optimized pie charts
5
+
6
+ @license
7
+ @author Robert Fleischmann <rendro87@gmail.com> (http://robert-fleischmann.de)
8
+ @version 2.1.6
9
+ ###
10
+ ((root, factory) ->
11
+ if typeof exports is "object"
12
+ module.exports = factory()
13
+ else if typeof define is "function" and define.amd
14
+ define [], factory
15
+ else
16
+ root["EasyPieChart"] = factory()
17
+ return
18
+ ) this, ->
19
+
20
+ ###*
21
+ Renderer to render the chart on a canvas object
22
+ @param {DOMElement} el DOM element to host the canvas (root of the plugin)
23
+ @param {object} options options object of the plugin
24
+ ###
25
+ CanvasRenderer = (el, options) ->
26
+ cachedBackground = undefined
27
+ canvas = document.createElement("canvas")
28
+ el.appendChild canvas
29
+ G_vmlCanvasManager.initElement canvas if typeof (G_vmlCanvasManager) isnt "undefined"
30
+ ctx = canvas.getContext("2d")
31
+ canvas.width = canvas.height = options.size
32
+
33
+ # canvas on retina devices
34
+ scaleBy = 1
35
+ if window.devicePixelRatio > 1
36
+ scaleBy = window.devicePixelRatio
37
+ canvas.style.width = canvas.style.height = [
38
+ options.size
39
+ "px"
40
+ ].join("")
41
+ canvas.width = canvas.height = options.size * scaleBy
42
+ ctx.scale scaleBy, scaleBy
43
+
44
+ # move 0,0 coordinates to the center
45
+ ctx.translate options.size / 2, options.size / 2
46
+
47
+ # rotate canvas -90deg
48
+ ctx.rotate (-1 / 2 + options.rotate / 180) * Math.PI
49
+ radius = (options.size - options.lineWidth) / 2
50
+ radius -= options.scaleLength + 2 if options.scaleColor and options.scaleLength # 2 is the distance between scale and bar
51
+
52
+ # IE polyfill for Date
53
+ Date.now = Date.now or ->
54
+ +(new Date())
55
+
56
+
57
+ ###*
58
+ Draw a circle around the center of the canvas
59
+ @param {strong} color Valid CSS color string
60
+ @param {number} lineWidth Width of the line in px
61
+ @param {number} percent Percentage to draw (float between -1 and 1)
62
+ ###
63
+ drawCircle = (color, lineWidth, percent) ->
64
+ percent = Math.min(Math.max(-1, percent or 0), 1)
65
+ isNegative = (if percent <= 0 then true else false)
66
+ ctx.beginPath()
67
+ ctx.arc 0, 0, radius, 0, Math.PI * 2 * percent, isNegative
68
+ ctx.strokeStyle = color
69
+ ctx.lineWidth = lineWidth
70
+ ctx.stroke()
71
+ return
72
+
73
+
74
+ ###*
75
+ Draw the scale of the chart
76
+ ###
77
+ drawScale = ->
78
+ offset = undefined
79
+ length = undefined
80
+ ctx.lineWidth = 1
81
+ ctx.fillStyle = options.scaleColor
82
+ ctx.save()
83
+ i = 24
84
+
85
+ while i > 0
86
+ if i % 6 is 0
87
+ length = options.scaleLength
88
+ offset = 0
89
+ else
90
+ length = options.scaleLength * 0.6
91
+ offset = options.scaleLength - length
92
+ ctx.fillRect -options.size / 2 + offset, 0, length, 1
93
+ ctx.rotate Math.PI / 12
94
+ --i
95
+ ctx.restore()
96
+ return
97
+
98
+
99
+ ###*
100
+ Request animation frame wrapper with polyfill
101
+ @return {function} Request animation frame method or timeout fallback
102
+ ###
103
+ reqAnimationFrame = (->
104
+ window.requestAnimationFrame or window.webkitRequestAnimationFrame or window.mozRequestAnimationFrame or (callback) ->
105
+ window.setTimeout callback, 1000 / 60
106
+ return
107
+ ())
108
+
109
+ ###*
110
+ Draw the background of the plugin including the scale and the track
111
+ ###
112
+ drawBackground = ->
113
+ drawScale() if options.scaleColor
114
+ drawCircle options.trackColor, options.trackWidth or options.lineWidth, 1 if options.trackColor
115
+ return
116
+
117
+
118
+ ###*
119
+ Canvas accessor
120
+ ###
121
+ @getCanvas = ->
122
+ canvas
123
+
124
+
125
+ ###*
126
+ Canvas 2D context 'ctx' accessor
127
+ ###
128
+ @getCtx = ->
129
+ ctx
130
+
131
+
132
+ ###*
133
+ Clear the complete canvas
134
+ ###
135
+ @clear = ->
136
+ ctx.clearRect options.size / -2, options.size / -2, options.size, options.size
137
+ return
138
+
139
+
140
+ ###*
141
+ Draw the complete chart
142
+ @param {number} percent Percent shown by the chart between -100 and 100
143
+ ###
144
+
145
+ # do we need to render a background
146
+
147
+ # getImageData and putImageData are supported
148
+
149
+ # if barcolor is a function execute it and pass the percent as a value
150
+
151
+ # draw bar
152
+ @draw = ((percent) ->
153
+ if !!options.scaleColor or !!options.trackColor
154
+ if ctx.getImageData and ctx.putImageData
155
+ unless cachedBackground
156
+ drawBackground()
157
+ cachedBackground = ctx.getImageData(0, 0, options.size * scaleBy, options.size * scaleBy)
158
+ else
159
+ ctx.putImageData cachedBackground, 0, 0
160
+ else
161
+ @clear()
162
+ drawBackground()
163
+ else
164
+ @clear()
165
+ ctx.lineCap = options.lineCap
166
+ color = undefined
167
+ if typeof (options.barColor) is "function"
168
+ color = options.barColor(percent)
169
+ else
170
+ color = options.barColor
171
+ drawCircle color, options.lineWidth, percent / 100
172
+ return
173
+ ).bind(this)
174
+
175
+ ###*
176
+ Animate from some percent to some other percentage
177
+ @param {number} from Starting percentage
178
+ @param {number} to Final percentage
179
+ ###
180
+ @animate = ((from, to) ->
181
+ startTime = Date.now()
182
+ options.onStart from, to
183
+ animation = (->
184
+ process = Math.min(Date.now() - startTime, options.animate.duration)
185
+ currentValue = options.easing(this, process, from, to - from, options.animate.duration)
186
+ @draw currentValue
187
+ options.onStep from, to, currentValue
188
+ if process >= options.animate.duration
189
+ options.onStop from, to
190
+ else
191
+ reqAnimationFrame animation
192
+ return
193
+ ).bind(this)
194
+ reqAnimationFrame animation
195
+ return
196
+ ).bind(this)
197
+ return
198
+
199
+ EasyPieChart = (el, opts) ->
200
+ defaultOptions =
201
+ barColor: "#ef1e25"
202
+ trackColor: "#f9f9f9"
203
+ scaleColor: "#dfe0e0"
204
+ scaleLength: 5
205
+ lineCap: "round"
206
+ lineWidth: 3
207
+ trackWidth: `undefined`
208
+ size: 110
209
+ rotate: 0
210
+ animate:
211
+ duration: 1000
212
+ enabled: true
213
+
214
+ easing: (x, t, b, c, d) -> # more can be found here: http://gsgd.co.uk/sandbox/jquery/easing/
215
+ t = t / (d / 2)
216
+ return c / 2 * t * t + b if t < 1
217
+ -c / 2 * ((--t) * (t - 2) - 1) + b
218
+
219
+ onStart: (from, to) ->
220
+ return
221
+
222
+ onStep: (from, to, currentValue) ->
223
+ return
224
+
225
+ onStop: (from, to) ->
226
+ return
227
+
228
+
229
+ # detect present renderer
230
+ if typeof (CanvasRenderer) isnt "undefined"
231
+ defaultOptions.renderer = CanvasRenderer
232
+ else if typeof (SVGRenderer) isnt "undefined"
233
+ defaultOptions.renderer = SVGRenderer
234
+ else
235
+ throw new Error("Please load either the SVG- or the CanvasRenderer")
236
+ options = {}
237
+ currentValue = 0
238
+
239
+ ###*
240
+ Initialize the plugin by creating the options object and initialize rendering
241
+ ###
242
+
243
+ # merge user options into default options
244
+
245
+ # check for jQuery easing
246
+
247
+ # process earlier animate option to avoid bc breaks
248
+
249
+ # create renderer
250
+
251
+ # initial draw
252
+
253
+ # initial update
254
+ init = (->
255
+ @el = el
256
+ @options = options
257
+ for i of defaultOptions
258
+ if defaultOptions.hasOwnProperty(i)
259
+ options[i] = (if opts and typeof (opts[i]) isnt "undefined" then opts[i] else defaultOptions[i])
260
+ options[i] = options[i].bind(this) if typeof (options[i]) is "function"
261
+ if typeof (options.easing) is "string" and typeof (jQuery) isnt "undefined" and jQuery.isFunction(jQuery.easing[options.easing])
262
+ options.easing = jQuery.easing[options.easing]
263
+ else
264
+ options.easing = defaultOptions.easing
265
+ if typeof (options.animate) is "number"
266
+ options.animate =
267
+ duration: options.animate
268
+ enabled: true
269
+ if typeof (options.animate) is "boolean" and not options.animate
270
+ options.animate =
271
+ duration: 1000
272
+ enabled: options.animate
273
+ @renderer = new options.renderer(el, options)
274
+ @renderer.draw currentValue
275
+ if el.dataset and el.dataset.percent
276
+ @update parseFloat(el.dataset.percent)
277
+ else @update parseFloat(el.getAttribute("data-percent")) if el.getAttribute and el.getAttribute("data-percent")
278
+ return
279
+ ).bind(this)
280
+
281
+ ###*
282
+ Update the value of the chart
283
+ @param {number} newValue Number between 0 and 100
284
+ @return {object} Instance of the plugin for method chaining
285
+ ###
286
+ @update = ((newValue) ->
287
+ newValue = parseFloat(newValue)
288
+ if options.animate.enabled
289
+ @renderer.animate currentValue, newValue
290
+ else
291
+ @renderer.draw newValue
292
+ currentValue = newValue
293
+ this
294
+ ).bind(this)
295
+
296
+ ###*
297
+ Disable animation
298
+ @return {object} Instance of the plugin for method chaining
299
+ ###
300
+ @disableAnimation = ->
301
+ options.animate.enabled = false
302
+ this
303
+
304
+
305
+ ###*
306
+ Enable animation
307
+ @return {object} Instance of the plugin for method chaining
308
+ ###
309
+ @enableAnimation = ->
310
+ options.animate.enabled = true
311
+ this
312
+
313
+ init()
314
+ return
315
+
316
+ EasyPieChart
@@ -0,0 +1,10 @@
1
+ .easyPieChart {
2
+ position: relative;
3
+ text-align: center;
4
+ }
5
+
6
+ .easyPieChart canvas {
7
+ position: absolute;
8
+ top: 0;
9
+ left: 0;
10
+ }