canvas.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /**
  2. * Renderer to render the chart on a canvas object
  3. * @param {DOMElement} el DOM element to host the canvas (root of the plugin)
  4. * @param {object} options options object of the plugin
  5. */
  6. var CanvasRenderer = function(el, options) {
  7. var cachedBackground;
  8. var canvas = document.createElement('canvas');
  9. el.appendChild(canvas);
  10. if (typeof(G_vmlCanvasManager) !== 'undefined') {
  11. G_vmlCanvasManager.initElement(canvas);
  12. }
  13. var ctx = canvas.getContext('2d');
  14. canvas.width = canvas.height = options.size;
  15. // canvas on retina devices
  16. var scaleBy = 1;
  17. if (window.devicePixelRatio > 1) {
  18. scaleBy = window.devicePixelRatio;
  19. canvas.style.width = canvas.style.height = [options.size, 'px'].join('');
  20. canvas.width = canvas.height = options.size * scaleBy;
  21. ctx.scale(scaleBy, scaleBy);
  22. }
  23. // move 0,0 coordinates to the center
  24. ctx.translate(options.size / 2, options.size / 2);
  25. // rotate canvas -90deg
  26. ctx.rotate((-1 / 2 + options.rotate / 180) * Math.PI);
  27. var radius = (options.size - options.lineWidth) / 2;
  28. if (options.scaleColor && options.scaleLength) {
  29. radius -= options.scaleLength + 2; // 2 is the distance between scale and bar
  30. }
  31. // IE polyfill for Date
  32. Date.now = Date.now || function() {
  33. return +(new Date());
  34. };
  35. /**
  36. * Draw a circle around the center of the canvas
  37. * @param {strong} color Valid CSS color string
  38. * @param {number} lineWidth Width of the line in px
  39. * @param {number} percent Percentage to draw (float between -1 and 1)
  40. */
  41. var drawCircle = function(color, lineWidth, percent) {
  42. percent = Math.min(Math.max(-1, percent || 0), 1);
  43. var isNegative = percent <= 0 ? true : false;
  44. ctx.beginPath();
  45. ctx.arc(0, 0, radius, 0, Math.PI * 2 * percent, isNegative);
  46. ctx.strokeStyle = color;
  47. ctx.lineWidth = lineWidth;
  48. ctx.stroke();
  49. };
  50. /**
  51. * Draw the scale of the chart
  52. */
  53. var drawScale = function() {
  54. var offset;
  55. var length;
  56. ctx.lineWidth = 1;
  57. ctx.fillStyle = options.scaleColor;
  58. ctx.save();
  59. for (var i = 24; i > 0; --i) {
  60. if (i % 6 === 0) {
  61. length = options.scaleLength;
  62. offset = 0;
  63. } else {
  64. length = options.scaleLength * 0.6;
  65. offset = options.scaleLength - length;
  66. }
  67. ctx.fillRect(-options.size/2 + offset, 0, length, 1);
  68. ctx.rotate(Math.PI / 12);
  69. }
  70. ctx.restore();
  71. };
  72. /**
  73. * Request animation frame wrapper with polyfill
  74. * @return {function} Request animation frame method or timeout fallback
  75. */
  76. var reqAnimationFrame = (function() {
  77. return window.requestAnimationFrame ||
  78. window.webkitRequestAnimationFrame ||
  79. window.mozRequestAnimationFrame ||
  80. function(callback) {
  81. window.setTimeout(callback, 1000 / 60);
  82. };
  83. }());
  84. /**
  85. * Draw the background of the plugin including the scale and the track
  86. */
  87. var drawBackground = function() {
  88. if(options.scaleColor) drawScale();
  89. if(options.trackColor) drawCircle(options.trackColor, options.trackWidth || options.lineWidth, 1);
  90. };
  91. /**
  92. * Canvas accessor
  93. */
  94. this.getCanvas = function() {
  95. return canvas;
  96. };
  97. /**
  98. * Canvas 2D context 'ctx' accessor
  99. */
  100. this.getCtx = function() {
  101. return ctx;
  102. };
  103. /**
  104. * Clear the complete canvas
  105. */
  106. this.clear = function() {
  107. ctx.clearRect(options.size / -2, options.size / -2, options.size, options.size);
  108. };
  109. /**
  110. * Draw the complete chart
  111. * @param {number} percent Percent shown by the chart between -100 and 100
  112. */
  113. this.draw = function(percent) {
  114. // do we need to render a background
  115. if (!!options.scaleColor || !!options.trackColor) {
  116. // getImageData and putImageData are supported
  117. if (ctx.getImageData && ctx.putImageData) {
  118. if (!cachedBackground) {
  119. drawBackground();
  120. cachedBackground = ctx.getImageData(0, 0, options.size * scaleBy, options.size * scaleBy);
  121. } else {
  122. ctx.putImageData(cachedBackground, 0, 0);
  123. }
  124. } else {
  125. this.clear();
  126. drawBackground();
  127. }
  128. } else {
  129. this.clear();
  130. }
  131. ctx.lineCap = options.lineCap;
  132. // if barcolor is a function execute it and pass the percent as a value
  133. var color;
  134. if (typeof(options.barColor) === 'function') {
  135. color = options.barColor(percent);
  136. } else {
  137. color = options.barColor;
  138. }
  139. // draw bar
  140. drawCircle(color, options.lineWidth, percent / 100);
  141. }.bind(this);
  142. /**
  143. * Animate from some percent to some other percentage
  144. * @param {number} from Starting percentage
  145. * @param {number} to Final percentage
  146. */
  147. this.animate = function(from, to) {
  148. var startTime = Date.now();
  149. options.onStart(from, to);
  150. var animation = function() {
  151. var process = Math.min(Date.now() - startTime, options.animate.duration);
  152. var currentValue = options.easing(this, process, from, to - from, options.animate.duration);
  153. this.draw(currentValue);
  154. options.onStep(from, to, currentValue);
  155. if (process >= options.animate.duration) {
  156. options.onStop(from, to);
  157. } else {
  158. reqAnimationFrame(animation);
  159. }
  160. }.bind(this);
  161. reqAnimationFrame(animation);
  162. }.bind(this);
  163. };