graphs.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. //Creates a line graph within a canvas
  2. //Will expand or shrink to the size of the canvas
  3. //Inputs:
  4. // canvas = the canvas that you would like to draw on
  5. // yName = a string for the name of the Y axis
  6. // xName = a string for the name of the X axis
  7. class LineGraph{
  8. constructor(canvas, yName, xName){
  9. canvas.height = canvas.clientHeight;
  10. canvas.width = canvas.clientWidth;
  11. this.canvas = canvas;
  12. this.context = canvas.getContext("2d");
  13. this.left = canvas.clientWidth - (canvas.clientWidth * 0.9);
  14. this.right = canvas.clientWidth * 0.8;
  15. this.top = canvas.clientHeight - (canvas.clientHeight * 0.99);
  16. this.bottom = canvas.clientHeight * 0.9;
  17. this.data = [];
  18. this.max = 0;
  19. this.xName = xName;
  20. this.yName = yName;
  21. this.xRange = [];
  22. this.colors = [];
  23. this.colorIndex = 0;
  24. for(let i = 0; i < 100; i++){
  25. let redRand = Math.floor(Math.random() * 200);
  26. let greenRand = Math.floor(Math.random() * 200);
  27. let blueRand = Math.floor(Math.random() * 200);
  28. this.colors.push(`rgb(${redRand}, ${greenRand}, ${blueRand})`);
  29. }
  30. }
  31. //Add a dataset to the graph to draw
  32. //Inputs:
  33. // data = array containing list of numbers as the data points for the graph
  34. // data[0] will be on the left. data[data.length-1] will be on the right.
  35. // xRange = array containing two elements, start and end for x axis data (currently only dates)
  36. // name = string name for the line. Used for display and finding lines. Each must be unique
  37. addData(data, xRange, name){
  38. console.log(data);
  39. data = {
  40. set: data,
  41. colorIndex: this.colorIndex,
  42. name: name
  43. }
  44. this.colorIndex++;
  45. this.data.push(data);
  46. let isChange = false;
  47. for(let point of data.set){
  48. if(point > this.max){
  49. this.max = point;
  50. this.verticalMultiplier = (this.bottom - this.top) / this.max;
  51. this.horizontalMultiplier = (this.right - this.left) / (data.set.length - 1);
  52. isChange = true;
  53. }
  54. }
  55. if(this.xRange.length === 0){
  56. this.xRange = xRange;
  57. isChange = true;
  58. }else{
  59. if(xRange[0] < this.xRange[0]){
  60. this.xRange[0] = xRange[0];
  61. isChange = true;
  62. }
  63. if(xRange[1] > this.xRange[1]){
  64. this.xRange[1] = xRange[1];
  65. isChange = true;
  66. }
  67. }
  68. if(isChange){
  69. this.drawGraph();
  70. }else{
  71. this.drawLine(data);
  72. }
  73. }
  74. //Removes a single data set from the graph and its line
  75. //Inputs:
  76. // id = the unique identifier of the data set that was passed in with addData function
  77. removeData(name){
  78. for(let i = 0; i < this.data.length; i++){
  79. if(this.data[i].name === name){
  80. this.data.splice(i, 1);
  81. break;
  82. }
  83. }
  84. this.drawGraph();
  85. }
  86. //Completely clears all data
  87. //Does not delete the current graph displaying
  88. clearData(){
  89. this.max = 0;
  90. this.data = [];
  91. this.xRange = [];
  92. }
  93. /**********
  94. *********PRIVATE*********
  95. **********/
  96. drawGraph(){
  97. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  98. this.drawYAxis();
  99. this.drawXAxis();
  100. for(let dataSet of this.data){
  101. this.drawLine(dataSet);
  102. }
  103. }
  104. drawLine(data){
  105. for(let i = 0; i < data.set.length - 1; i++){
  106. this.context.beginPath();
  107. this.context.moveTo(this.left + (this.horizontalMultiplier * i), this.bottom - (this.verticalMultiplier * data.set[i]));
  108. this.context.lineTo(this.left + (this.horizontalMultiplier * (i + 1)), this.bottom - (this.verticalMultiplier * data.set[i + 1]));
  109. this.context.strokeStyle = this.colors[data.colorIndex];
  110. this.context.lineWidth = 2;
  111. this.context.stroke();
  112. }
  113. this.context.strokeStyle = "black";
  114. this.drawLegend(data.colorIndex, data.name);
  115. }
  116. drawXAxis(){
  117. this.context.beginPath();
  118. this.context.moveTo(this.left, this.bottom);
  119. this.context.lineTo(this.right, this.bottom);
  120. this.context.lineWidth = 4;
  121. this.context.stroke();
  122. this.context.font = "25px Arial";
  123. this.context.fillText(this.xName, this.right / 2, this.bottom + 50);
  124. this.context.setLineDash([5, 10]);
  125. this.context.font = "10px Arial";
  126. this.context.lineWidth = 1;
  127. if(Object.prototype.toString.call(this.xRange[0]) === '[object Date]'){
  128. let diff = Math.abs(Math.floor((Date.UTC(this.xRange[0].getFullYear(), this.xRange[0].getMonth(), this.xRange[0].getDate()) - Date.UTC(this.xRange[1].getFullYear(), this.xRange[1].getMonth(), this.xRange[1].getDate())) / (1000 * 60 * 60 * 24))) + 1;
  129. let showDate = new Date(this.xRange[0]);
  130. for(let i = 0; i < diff; i += Math.floor(diff / 10)){
  131. this.context.fillText(showDate.toLocaleDateString("en-US", {month: "short", day: "numeric", year: "2-digit"}), this.left + (this.horizontalMultiplier * i) - 20, this.bottom + 15);
  132. if(i !== 0){
  133. this.context.beginPath()
  134. this.context.moveTo(this.left + (this.horizontalMultiplier * i), this.bottom);
  135. this.context.lineTo(this.left + (this.horizontalMultiplier * i), this.top);
  136. this.context.strokeStyle = "#a5a5a5";
  137. this.context.stroke();
  138. }
  139. showDate.setDate(showDate.getDate() + Math.abs(diff / 10));
  140. }
  141. }
  142. this.context.strokeStyle = "black";
  143. this.context.setLineDash([]);
  144. }
  145. drawYAxis(){
  146. this.context.beginPath();
  147. this.context.moveTo(this.left, this.top);
  148. this.context.lineTo(this.left, this.bottom);
  149. this.context.lineWidth = 2;
  150. this.context.stroke();
  151. this.context.font = "25px Arial";
  152. this.context.fillText(this.yName, 0, this.bottom / 2);
  153. this.context.setLineDash([5, 10]);
  154. this.context.font = "10px Arial";
  155. this.context.lineWidth = 1;
  156. let axisNum = 0;
  157. let verticalIncrement = (this.bottom - this.top) / 10;
  158. let verticalOffset = 0;
  159. do{
  160. this.context.fillText(Math.round(axisNum).toString(), this.left - 20, this.bottom - verticalOffset + 3);
  161. this.context.beginPath();
  162. this.context.moveTo(this.left, this.bottom - verticalOffset);
  163. this.context.lineTo(this.right, this.bottom - verticalOffset);
  164. this.context.strokeStyle = "#a5a5a5";
  165. this.context.stroke();
  166. verticalOffset += verticalIncrement;
  167. axisNum += this.max / 10;
  168. }while(verticalOffset <= (this.bottom - this.top));
  169. this.context.strokeStyle = "black";
  170. this.context.setLineDash([]);
  171. }
  172. drawLegend(colorIndex, name){
  173. let verticalOffset;
  174. for(let i = 0; i < this.data.length; i++){
  175. if(this.data[i].name === name){
  176. verticalOffset = i * 25;
  177. break;
  178. }
  179. }
  180. this.context.beginPath();
  181. this.context.fillStyle = this.colors[colorIndex];
  182. this.context.fillRect(this.right + 50, this.top + 50 + verticalOffset, 10, 10);
  183. this.context.stroke();
  184. this.context.font = "15px Arial";
  185. this.context.fillText(name, this.right + 65, this.top + 60 + verticalOffset);
  186. this.context.fillStyle = "black";
  187. }
  188. }