easy_pie_chart 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }