radialIndicator.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. radialIndicator.js v 1.0.0
  3. Author: Sudhanshu Yadav
  4. Copyright (c) 2015 Sudhanshu Yadav - ignitersworld.com , released under the MIT license.
  5. Demo on: ignitersworld.com/lab/radialIndicator.html
  6. */
  7. ;(function ($, window, document) {
  8. "use strict";
  9. //circumfence and quart value to start bar from top
  10. var circ = Math.PI * 2,
  11. quart = Math.PI / 2;
  12. //function to convert hex to rgb
  13. function hexToRgb(hex) {
  14. // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
  15. var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  16. hex = hex.replace(shorthandRegex, function (m, r, g, b) {
  17. return r + r + g + g + b + b;
  18. });
  19. var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  20. return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null;
  21. }
  22. function getPropVal(curShift, perShift, bottomRange, topRange) {
  23. return Math.round(bottomRange + ((topRange - bottomRange) * curShift / perShift));
  24. }
  25. //function to get current color in case of
  26. function getCurrentColor(curPer, bottomVal, topVal, bottomColor, topColor) {
  27. var rgbAryTop = topColor.indexOf('#') != -1 ? hexToRgb(topColor) : topColor.match(/\d+/g),
  28. rgbAryBottom = bottomColor.indexOf('#') != -1 ? hexToRgb(bottomColor) : bottomColor.match(/\d+/g),
  29. perShift = topVal - bottomVal,
  30. curShift = curPer - bottomVal;
  31. if (!rgbAryTop || !rgbAryBottom) return null;
  32. return 'rgb(' + getPropVal(curShift, perShift, rgbAryBottom[0], rgbAryTop[0]) + ',' + getPropVal(curShift, perShift, rgbAryBottom[1], rgbAryTop[1]) + ',' + getPropVal(curShift, perShift, rgbAryBottom[2], rgbAryTop[2]) + ')';
  33. }
  34. //to merge object
  35. function merge() {
  36. var arg = arguments,
  37. target = arg[0];
  38. for (var i = 1, ln = arg.length; i < ln; i++) {
  39. var obj = arg[i];
  40. for (var k in obj) {
  41. if (obj.hasOwnProperty(k)) {
  42. target[k] = obj[k];
  43. }
  44. }
  45. }
  46. return target;
  47. }
  48. //function to apply formatting on number depending on parameter
  49. function formatter(pattern) {
  50. return function (num) {
  51. if(!pattern) return num.toString();
  52. num = num || 0
  53. var numRev = num.toString().split('').reverse(),
  54. output = pattern.split("").reverse(),
  55. i = 0,
  56. lastHashReplaced = 0;
  57. //changes hash with numbers
  58. for (var ln = output.length; i < ln; i++) {
  59. if (!numRev.length) break;
  60. if (output[i] == "#") {
  61. lastHashReplaced = i;
  62. output[i] = numRev.shift();
  63. }
  64. }
  65. //add overflowing numbers before prefix
  66. output.splice(lastHashReplaced+1, output.lastIndexOf('#') - lastHashReplaced, numRev.reverse().join(""));
  67. return output.reverse().join('');
  68. }
  69. }
  70. //circle bar class
  71. function Indicator(container, indOption) {
  72. indOption = indOption || {};
  73. indOption = merge({}, radialIndicator.defaults, indOption);
  74. this.indOption = indOption;
  75. //create a queryselector if a selector string is passed in container
  76. if (typeof container == "string")
  77. container = document.querySelector(container);
  78. //get the first element if container is a node list
  79. if (container.length)
  80. container = container[0];
  81. this.container = container;
  82. //create a canvas element
  83. var canElm = document.createElement("canvas");
  84. container.appendChild(canElm);
  85. this.canElm = canElm; // dom object where drawing will happen
  86. this.ctx = canElm.getContext('2d'); //get 2d canvas context
  87. //add intial value
  88. this.current_value = indOption.initValue || indOption.minValue || 0;
  89. }
  90. Indicator.prototype = {
  91. constructor: radialIndicator,
  92. init: function () {
  93. var indOption = this.indOption,
  94. canElm = this.canElm,
  95. ctx = this.ctx,
  96. dim = (indOption.radius + indOption.barWidth) * 2, //elm width and height
  97. center = dim / 2; //center point in both x and y axis
  98. //create a formatter function
  99. this.formatter = typeof indOption.format == "function" ? indOption.format : formatter(indOption.format);
  100. //maximum text length;
  101. this.maxLength = indOption.percentage ? 4 : this.formatter(indOption.maxValue).length;
  102. canElm.width = dim;
  103. canElm.height = dim;
  104. //draw a grey circle
  105. ctx.strokeStyle = indOption.barBgColor; //background circle color
  106. ctx.lineWidth = indOption.barWidth;
  107. ctx.beginPath();
  108. ctx.arc(center, center, indOption.radius, 0, 2 * Math.PI);
  109. ctx.stroke();
  110. //store the image data after grey circle draw
  111. this.imgData = ctx.getImageData(0, 0, dim, dim);
  112. //put the initial value if defined
  113. this.value(this.current_value);
  114. return this;
  115. },
  116. //update the value of indicator without animation
  117. value: function (val) {
  118. //return the val if val is not provided
  119. if (val === undefined || isNaN(val)) {
  120. return this.current_value;
  121. }
  122. val = parseInt(val);
  123. var ctx = this.ctx,
  124. indOption = this.indOption,
  125. curColor = indOption.barColor,
  126. dim = (indOption.radius + indOption.barWidth) * 2,
  127. minVal = indOption.minValue,
  128. maxVal = indOption.maxValue,
  129. center = dim / 2;
  130. //limit the val in range of 0 to 100
  131. val = val < minVal ? minVal : val > maxVal ? maxVal : val;
  132. var perVal = Math.round(((val - minVal) * 100 / (maxVal - minVal)) * 100) / 100, //percentage value tp two decimal precision
  133. dispVal = indOption.percentage ? perVal + '%' : this.formatter(val); //formatted value
  134. //save val on object
  135. this.current_value = val;
  136. //draw the bg circle
  137. ctx.putImageData(this.imgData, 0, 0);
  138. //get current color if color range is set
  139. if (typeof curColor == "object") {
  140. var range = Object.keys(curColor);
  141. for (var i = 1, ln = range.length; i < ln; i++) {
  142. var bottomVal = range[i - 1],
  143. topVal = range[i],
  144. bottomColor = curColor[bottomVal],
  145. topColor = curColor[topVal],
  146. newColor = val == bottomVal ? bottomColor : val == topVal ? topColor : val > bottomVal && val < topVal ? indOption.interpolate ? getCurrentColor(val, bottomVal, topVal, bottomColor, topColor) : topColor : false;
  147. if (newColor != false) {
  148. curColor = newColor;
  149. break;
  150. }
  151. }
  152. }
  153. //draw th circle value
  154. ctx.strokeStyle = curColor;
  155. //add linecap if value setted on options
  156. if (indOption.roundCorner) ctx.lineCap = "round";
  157. ctx.beginPath();
  158. ctx.arc(center, center, indOption.radius, -(quart), ((circ) * perVal / 100) - quart, false);
  159. ctx.stroke();
  160. //add percentage text
  161. if (indOption.displayNumber) {
  162. var cFont = ctx.font.split(' '),
  163. weight = indOption.fontWeight,
  164. fontSize = indOption.fontSize || (dim / (this.maxLength - (Math.floor(this.maxLength*1.4/4)-1)));
  165. cFont = indOption.fontFamily || cFont[cFont.length - 1];
  166. ctx.fillStyle = indOption.fontColor || curColor;
  167. ctx.font = weight +" "+ fontSize + "px " + cFont;
  168. ctx.textAlign = "center";
  169. ctx.textBaseline = 'middle';
  170. ctx.fillText(dispVal, center, center);
  171. }
  172. return this;
  173. },
  174. //animate progressbar to the value
  175. animate: function (val) {
  176. var indOption = this.indOption,
  177. counter = this.current_value || indOption.minValue,
  178. self = this,
  179. incBy = Math.ceil((indOption.maxValue - indOption.minValue) / (indOption.frameNum || (indOption.percentage ? 100 : 500))), //increment by .2% on every tick and 1% if showing as percentage
  180. back = val < counter;
  181. //clear interval function if already started
  182. if (this.intvFunc) clearInterval(this.intvFunc);
  183. this.intvFunc = setInterval(function () {
  184. if ((!back && counter >= val) || (back && counter <= val)) {
  185. if (self.current_value == counter) {
  186. clearInterval(self.intvFunc);
  187. return;
  188. } else {
  189. counter = val;
  190. }
  191. }
  192. self.value(counter); //dispaly the value
  193. if (counter != val) {
  194. counter = counter + (back ? -incBy : incBy)
  195. }; //increment or decrement till counter does not reach to value
  196. }, indOption.frameTime);
  197. return this;
  198. },
  199. //method to update options
  200. option: function (key, val) {
  201. if (val === undefined) return this.option[key];
  202. if (['radius', 'barWidth', 'barBgColor', 'format', 'maxValue', 'percentage'].indexOf(key) != -1) {
  203. this.indOption[key] = val;
  204. this.init().value(this.current_value);
  205. }
  206. this.indOption[key] = val;
  207. }
  208. };
  209. /** Initializer function **/
  210. function radialIndicator(container, options) {
  211. var progObj = new Indicator(container, options);
  212. progObj.init();
  213. return progObj;
  214. }
  215. //radial indicator defaults
  216. radialIndicator.defaults = {
  217. radius: 50, //inner radius of indicator
  218. barWidth: 5, //bar width
  219. barBgColor: '#eeeeee', //unfilled bar color
  220. barColor: '#99CC33', //filled bar color , can be a range also having different colors on different value like {0 : "#ccc", 50 : '#333', 100: '#000'}
  221. format: null, //format indicator numbers, can be a # formator ex (##,###.##) or a function
  222. frameTime: 10, //miliseconds to move from one frame to another
  223. frameNum: null, //Defines numbers of frame in indicator, defaults to 100 when showing percentage and 500 for other values
  224. fontColor: null, //font color
  225. fontFamily: null, //defines font family
  226. fontWeight: 'bold', //defines font weight
  227. fontSize : null, //define the font size of indicator number
  228. interpolate: true, //interpolate color between ranges
  229. percentage: false, //show percentage of value
  230. displayNumber: true, //display indicator number
  231. roundCorner: false, //have round corner in filled bar
  232. minValue: 0, //minimum value
  233. maxValue: 100, //maximum value
  234. initValue: 0 //define initial value of indicator
  235. };
  236. window.radialIndicator = radialIndicator;
  237. //add as a jquery plugin
  238. if ($) {
  239. $.fn.radialIndicator = function (options) {
  240. return this.each(function () {
  241. var newPCObj = radialIndicator(this, options);
  242. $.data(this, 'radialIndicator', newPCObj);
  243. });
  244. };
  245. }
  246. }(window.jQuery, window, document, void 0));