Chart.Doughnut.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. (function(){
  2. "use strict";
  3. var root = this,
  4. Chart = root.Chart,
  5. //Cache a local reference to Chart.helpers
  6. helpers = Chart.helpers;
  7. var defaultConfig = {
  8. //Boolean - Whether we should show a stroke on each segment
  9. segmentShowStroke : true,
  10. //String - The colour of each segment stroke
  11. segmentStrokeColor : "#fff",
  12. //Number - The width of each segment stroke
  13. segmentStrokeWidth : 2,
  14. //The percentage of the chart that we cut out of the middle.
  15. percentageInnerCutout : 50,
  16. //Number - Amount of animation steps
  17. animationSteps : 100,
  18. //String - Animation easing effect
  19. animationEasing : "easeOutBounce",
  20. //Boolean - Whether we animate the rotation of the Doughnut
  21. animateRotate : true,
  22. //Boolean - Whether we animate scaling the Doughnut from the centre
  23. animateScale : false,
  24. //String - A legend template
  25. 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>"
  26. };
  27. Chart.Type.extend({
  28. //Passing in a name registers this chart in the Chart namespace
  29. name: "Doughnut",
  30. //Providing a defaults will also register the deafults in the chart namespace
  31. defaults : defaultConfig,
  32. //Initialize is fired when the chart is initialized - Data is passed in as a parameter
  33. //Config is automatically merged by the core of Chart.js, and is available at this.options
  34. initialize: function(data){
  35. //Declare segments as a static property to prevent inheriting across the Chart type prototype
  36. this.segments = [];
  37. this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
  38. this.SegmentArc = Chart.Arc.extend({
  39. ctx : this.chart.ctx,
  40. x : this.chart.width/2,
  41. y : this.chart.height/2
  42. });
  43. //Set up tooltip events on the chart
  44. if (this.options.showTooltips){
  45. helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
  46. var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
  47. helpers.each(this.segments,function(segment){
  48. segment.restore(["fillColor"]);
  49. });
  50. helpers.each(activeSegments,function(activeSegment){
  51. activeSegment.fillColor = activeSegment.highlightColor;
  52. });
  53. this.showTooltip(activeSegments);
  54. });
  55. }
  56. this.calculateTotal(data);
  57. helpers.each(data,function(datapoint, index){
  58. this.addData(datapoint, index, true);
  59. },this);
  60. this.render();
  61. },
  62. getSegmentsAtEvent : function(e){
  63. var segmentsArray = [];
  64. var location = helpers.getRelativePosition(e);
  65. helpers.each(this.segments,function(segment){
  66. if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
  67. },this);
  68. return segmentsArray;
  69. },
  70. addData : function(segment, atIndex, silent){
  71. var index = atIndex || this.segments.length;
  72. this.segments.splice(index, 0, new this.SegmentArc({
  73. value : segment.value,
  74. outerRadius : (this.options.animateScale) ? 0 : this.outerRadius,
  75. innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,
  76. fillColor : segment.color,
  77. highlightColor : segment.highlight || segment.color,
  78. showStroke : this.options.segmentShowStroke,
  79. strokeWidth : this.options.segmentStrokeWidth,
  80. strokeColor : this.options.segmentStrokeColor,
  81. startAngle : Math.PI * 1.5,
  82. circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
  83. label : segment.label
  84. }));
  85. if (!silent){
  86. this.reflow();
  87. this.update();
  88. }
  89. },
  90. calculateCircumference : function(value){
  91. return (Math.PI*2)*(Math.abs(value) / this.total);
  92. },
  93. calculateTotal : function(data){
  94. this.total = 0;
  95. helpers.each(data,function(segment){
  96. this.total += Math.abs(segment.value);
  97. },this);
  98. },
  99. update : function(){
  100. this.calculateTotal(this.segments);
  101. // Reset any highlight colours before updating.
  102. helpers.each(this.activeElements, function(activeElement){
  103. activeElement.restore(['fillColor']);
  104. });
  105. helpers.each(this.segments,function(segment){
  106. segment.save();
  107. });
  108. this.render();
  109. },
  110. removeData: function(atIndex){
  111. var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
  112. this.segments.splice(indexToDelete, 1);
  113. this.reflow();
  114. this.update();
  115. },
  116. reflow : function(){
  117. helpers.extend(this.SegmentArc.prototype,{
  118. x : this.chart.width/2,
  119. y : this.chart.height/2
  120. });
  121. this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
  122. helpers.each(this.segments, function(segment){
  123. segment.update({
  124. outerRadius : this.outerRadius,
  125. innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
  126. });
  127. }, this);
  128. },
  129. draw : function(easeDecimal){
  130. var animDecimal = (easeDecimal) ? easeDecimal : 1;
  131. this.clear();
  132. helpers.each(this.segments,function(segment,index){
  133. segment.transition({
  134. circumference : this.calculateCircumference(segment.value),
  135. outerRadius : this.outerRadius,
  136. innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
  137. },animDecimal);
  138. segment.endAngle = segment.startAngle + segment.circumference;
  139. segment.draw();
  140. if (index === 0){
  141. segment.startAngle = Math.PI * 1.5;
  142. }
  143. //Check to see if it's the last segment, if not get the next and update the start angle
  144. if (index < this.segments.length-1){
  145. this.segments[index+1].startAngle = segment.endAngle;
  146. }
  147. },this);
  148. }
  149. });
  150. Chart.types.Doughnut.extend({
  151. name : "Pie",
  152. defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})
  153. });
  154. }).call(this);