element.line.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. 'use strict';
  2. module.exports = function(Chart) {
  3. var helpers = Chart.helpers;
  4. var globalDefaults = Chart.defaults.global;
  5. Chart.defaults.global.elements.line = {
  6. tension: 0.4,
  7. backgroundColor: globalDefaults.defaultColor,
  8. borderWidth: 3,
  9. borderColor: globalDefaults.defaultColor,
  10. borderCapStyle: 'butt',
  11. borderDash: [],
  12. borderDashOffset: 0.0,
  13. borderJoinStyle: 'miter',
  14. capBezierPoints: true,
  15. fill: true, // do we fill in the area between the line and its base axis
  16. };
  17. Chart.elements.Line = Chart.Element.extend({
  18. draw: function() {
  19. var me = this;
  20. var vm = me._view;
  21. var spanGaps = vm.spanGaps;
  22. var fillPoint = vm.scaleZero;
  23. var loop = me._loop;
  24. // Handle different fill modes for cartesian lines
  25. if (!loop) {
  26. if (vm.fill === 'top') {
  27. fillPoint = vm.scaleTop;
  28. } else if (vm.fill === 'bottom') {
  29. fillPoint = vm.scaleBottom;
  30. }
  31. }
  32. var ctx = me._chart.ctx;
  33. ctx.save();
  34. // Helper function to draw a line to a point
  35. function lineToPoint(previousPoint, point) {
  36. var pointVM = point._view;
  37. if (point._view.steppedLine === true) {
  38. ctx.lineTo(pointVM.x, previousPoint._view.y);
  39. ctx.lineTo(pointVM.x, pointVM.y);
  40. } else if (point._view.tension === 0) {
  41. ctx.lineTo(pointVM.x, pointVM.y);
  42. } else {
  43. ctx.bezierCurveTo(
  44. previousPoint._view.controlPointNextX,
  45. previousPoint._view.controlPointNextY,
  46. pointVM.controlPointPreviousX,
  47. pointVM.controlPointPreviousY,
  48. pointVM.x,
  49. pointVM.y
  50. );
  51. }
  52. }
  53. var points = me._children.slice(); // clone array
  54. var lastDrawnIndex = -1;
  55. // If we are looping, adding the first point again
  56. if (loop && points.length) {
  57. points.push(points[0]);
  58. }
  59. var index, current, previous, currentVM;
  60. // Fill Line
  61. if (points.length && vm.fill) {
  62. ctx.beginPath();
  63. for (index = 0; index < points.length; ++index) {
  64. current = points[index];
  65. previous = helpers.previousItem(points, index);
  66. currentVM = current._view;
  67. // First point moves to it's starting position no matter what
  68. if (index === 0) {
  69. if (loop) {
  70. ctx.moveTo(fillPoint.x, fillPoint.y);
  71. } else {
  72. ctx.moveTo(currentVM.x, fillPoint);
  73. }
  74. if (!currentVM.skip) {
  75. lastDrawnIndex = index;
  76. ctx.lineTo(currentVM.x, currentVM.y);
  77. }
  78. } else {
  79. previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
  80. if (currentVM.skip) {
  81. // Only do this if this is the first point that is skipped
  82. if (!spanGaps && lastDrawnIndex === (index - 1)) {
  83. if (loop) {
  84. ctx.lineTo(fillPoint.x, fillPoint.y);
  85. } else {
  86. ctx.lineTo(previous._view.x, fillPoint);
  87. }
  88. }
  89. } else {
  90. if (lastDrawnIndex !== (index - 1)) {
  91. // There was a gap and this is the first point after the gap. If we've never drawn a point, this is a special case.
  92. // If the first data point is NaN, then there is no real gap to skip
  93. if (spanGaps && lastDrawnIndex !== -1) {
  94. // We are spanning the gap, so simple draw a line to this point
  95. lineToPoint(previous, current);
  96. } else if (loop) {
  97. ctx.lineTo(currentVM.x, currentVM.y);
  98. } else {
  99. ctx.lineTo(currentVM.x, fillPoint);
  100. ctx.lineTo(currentVM.x, currentVM.y);
  101. }
  102. } else {
  103. // Line to next point
  104. lineToPoint(previous, current);
  105. }
  106. lastDrawnIndex = index;
  107. }
  108. }
  109. }
  110. if (!loop && lastDrawnIndex !== -1) {
  111. ctx.lineTo(points[lastDrawnIndex]._view.x, fillPoint);
  112. }
  113. ctx.fillStyle = vm.backgroundColor || globalDefaults.defaultColor;
  114. ctx.closePath();
  115. ctx.fill();
  116. }
  117. // Stroke Line Options
  118. var globalOptionLineElements = globalDefaults.elements.line;
  119. ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
  120. // IE 9 and 10 do not support line dash
  121. if (ctx.setLineDash) {
  122. ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
  123. }
  124. ctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;
  125. ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
  126. ctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;
  127. ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
  128. // Stroke Line
  129. ctx.beginPath();
  130. lastDrawnIndex = -1;
  131. for (index = 0; index < points.length; ++index) {
  132. current = points[index];
  133. previous = helpers.previousItem(points, index);
  134. currentVM = current._view;
  135. // First point moves to it's starting position no matter what
  136. if (index === 0) {
  137. if (!currentVM.skip) {
  138. ctx.moveTo(currentVM.x, currentVM.y);
  139. lastDrawnIndex = index;
  140. }
  141. } else {
  142. previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];
  143. if (!currentVM.skip) {
  144. if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
  145. // There was a gap and this is the first point after the gap
  146. ctx.moveTo(currentVM.x, currentVM.y);
  147. } else {
  148. // Line to next point
  149. lineToPoint(previous, current);
  150. }
  151. lastDrawnIndex = index;
  152. }
  153. }
  154. }
  155. ctx.stroke();
  156. ctx.restore();
  157. }
  158. });
  159. };