graphs.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. this.canvas = canvas;
  10. this.context = canvas.getContext("2d");
  11. this.left = canvas.clientWidth - (canvas.clientWidth * 0.9);
  12. this.right = canvas.clientWidth * 0.8;
  13. this.top = canvas.clientHeight - (canvas.clientHeight * 0.99);
  14. this.bottom = canvas.clientHeight * 0.9;
  15. this.data = [];
  16. this.max = 0;
  17. this.xName = xName;
  18. this.yName = yName;
  19. this.xRange = [];
  20. this.colors = [];
  21. this.colorIndex = 0;
  22. for(let i = 0; i < 100; i++){
  23. let redRand = Math.floor(Math.random() * 200);
  24. let greenRand = Math.floor(Math.random() * 200);
  25. let blueRand = Math.floor(Math.random() * 200);
  26. this.colors.push(`rgb(${redRand}, ${greenRand}, ${blueRand})`);
  27. }
  28. }
  29. //Add a dataset to the graph to draw
  30. //Inputs:
  31. // data = array containing list of numbers as the data points for the graph
  32. // data[0] will be on the left. data[data.length-1] will be on the right.
  33. // xRange = array containing two elements, start and end for x axis data (currently only dates)
  34. // name = string name for the line. Used for display and finding lines. Each must be unique
  35. addData(data, xRange, name){
  36. data = {
  37. set: data,
  38. colorIndex: this.colorIndex,
  39. name: name
  40. }
  41. this.colorIndex++;
  42. this.data.push(data);
  43. let isChange = false;
  44. for(let point of data.set){
  45. if(point > this.max){
  46. this.max = point;
  47. this.verticalMultiplier = (this.bottom - this.top) / this.max;
  48. this.horizontalMultiplier = (this.right - this.left) / (data.set.length - 1);
  49. isChange = true;
  50. }
  51. }
  52. if(this.xRange.length === 0){
  53. this.xRange = xRange;
  54. isChange = true;
  55. }else{
  56. if(xRange[0] < this.xRange[0]){
  57. this.xRange[0] = xRange[0];
  58. isChange = true;
  59. }
  60. if(xRange[1] > this.xRange[1]){
  61. this.xRange[1] = xRange[1];
  62. isChange = true;
  63. }
  64. }
  65. if(isChange){
  66. this.drawGraph();
  67. }else{
  68. this.drawLine(data);
  69. }
  70. }
  71. //Removes a single data set from the graph and its line
  72. //Inputs:
  73. // id = the unique identifier of the data set that was passed in with addData function
  74. removeData(name){
  75. for(let i = 0; i < this.data.length; i++){
  76. if(this.data[i].name === name){
  77. this.data.splice(i, 1);
  78. break;
  79. }
  80. }
  81. this.drawGraph();
  82. }
  83. //Completely clears all data
  84. //Does not delete the current graph displaying
  85. clearData(){
  86. this.max = 0;
  87. this.data = [];
  88. this.xRange = [];
  89. }
  90. /**********
  91. *********PRIVATE*********
  92. **********/
  93. drawGraph(){
  94. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  95. this.drawYAxis();
  96. this.drawXAxis();
  97. for(let dataSet of this.data){
  98. this.drawLine(dataSet);
  99. }
  100. }
  101. drawLine(data){
  102. for(let i = 0; i < data.set.length - 1; i++){
  103. this.context.beginPath();
  104. this.context.moveTo(this.left + (this.horizontalMultiplier * i), this.bottom - (this.verticalMultiplier * data.set[i]));
  105. this.context.lineTo(this.left + (this.horizontalMultiplier * (i + 1)), this.bottom - (this.verticalMultiplier * data.set[i + 1]));
  106. this.context.strokeStyle = this.colors[data.colorIndex];
  107. this.context.lineWidth = 2;
  108. this.context.stroke();
  109. }
  110. this.context.strokeStyle = "black";
  111. this.drawLegend(data.colorIndex, data.name);
  112. }
  113. drawXAxis(){
  114. this.context.beginPath();
  115. this.context.moveTo(this.left, this.bottom);
  116. this.context.lineTo(this.right, this.bottom);
  117. this.context.lineWidth = 4;
  118. this.context.stroke();
  119. this.context.font = "25px Arial";
  120. this.context.fillText(this.xName, this.right / 2, this.bottom + 50);
  121. this.context.setLineDash([5, 10]);
  122. this.context.font = "10px Arial";
  123. this.context.lineWidth = 1;
  124. if(Object.prototype.toString.call(this.xRange[0]) === '[object Date]'){
  125. 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;
  126. let showDate = new Date(this.xRange[0]);
  127. for(let i = 0; i < diff; i += Math.floor(diff / 10)){
  128. this.context.fillText(showDate.toLocaleDateString("en-US", {month: "short", day: "numeric", year: "2-digit"}), this.left + (this.horizontalMultiplier * i) - 20, this.bottom + 15);
  129. if(i !== 0){
  130. this.context.beginPath()
  131. this.context.moveTo(this.left + (this.horizontalMultiplier * i), this.bottom);
  132. this.context.lineTo(this.left + (this.horizontalMultiplier * i), this.top);
  133. this.context.strokeStyle = "#a5a5a5";
  134. this.context.stroke();
  135. }
  136. showDate.setDate(showDate.getDate() + Math.abs(diff / 10));
  137. }
  138. }
  139. this.context.strokeStyle = "black";
  140. this.context.setLineDash([]);
  141. }
  142. drawYAxis(){
  143. this.context.beginPath();
  144. this.context.moveTo(this.left, this.top);
  145. this.context.lineTo(this.left, this.bottom);
  146. this.context.lineWidth = 2;
  147. this.context.stroke();
  148. this.context.font = "25px Arial";
  149. this.context.fillText(this.yName, 0, this.bottom / 2);
  150. this.context.setLineDash([5, 10]);
  151. this.context.font = "10px Arial";
  152. this.context.lineWidth = 1;
  153. let axisNum = 0;
  154. let verticalIncrement = (this.bottom - this.top) / 10;
  155. let verticalOffset = 0;
  156. do{
  157. this.context.fillText(Math.round(axisNum).toString(), this.left - 20, this.bottom - verticalOffset + 3);
  158. this.context.beginPath();
  159. this.context.moveTo(this.left, this.bottom - verticalOffset);
  160. this.context.lineTo(this.right, this.bottom - verticalOffset);
  161. this.context.strokeStyle = "#a5a5a5";
  162. this.context.stroke();
  163. verticalOffset += verticalIncrement;
  164. axisNum += this.max / 10;
  165. }while(verticalOffset <= (this.bottom - this.top));
  166. this.context.strokeStyle = "black";
  167. this.context.setLineDash([]);
  168. }
  169. drawLegend(colorIndex, name){
  170. let verticalOffset;
  171. for(let i = 0; i < this.data.length; i++){
  172. if(this.data[i].name === name){
  173. verticalOffset = i * 25;
  174. break;
  175. }
  176. }
  177. this.context.beginPath();
  178. this.context.fillStyle = this.colors[colorIndex];
  179. this.context.fillRect(this.right + 50, this.top + 50 + verticalOffset, 10, 10);
  180. this.context.stroke();
  181. this.context.font = "15px Arial";
  182. this.context.fillText(name, this.right + 65, this.top + 60 + verticalOffset);
  183. this.context.fillStyle = "black";
  184. }
  185. }
  186. class HorizontalBarGraph{
  187. constructor(canvas){
  188. this.canvas = canvas;
  189. this.context = canvas.getContext("2d");
  190. this.left = 0;
  191. this.right = canvas.clientWidth;
  192. this.top = canvas.clientHeight - (canvas.clientHeight * 0.99);
  193. this.bottom = canvas.clientHeight;
  194. this.data = [];
  195. this.max = 0;
  196. }
  197. //Adds an array of data points to the chart
  198. //All data is removed and redrawn when called
  199. //Must pass in all data points
  200. //Inputs:
  201. // dataArray: array of objects
  202. // num: number for the actual data
  203. // label: text to display on bar
  204. addData(dataArray){
  205. let data = [];
  206. this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  207. for(let point of dataArray){
  208. if(point.num > this.max){
  209. this.max = point.num;
  210. }
  211. this.data.push(point);
  212. }
  213. this.drawGraph();
  214. }
  215. drawGraph(){
  216. let barHeight = (this.bottom - this.top) / this.data.length;
  217. for(let i = 0; i < this.data.length; i++){
  218. let topLocation = this.top + (i * barHeight) + 5;
  219. let width = (this.right - this.left) * (this.data[i].num / this.max);
  220. if(this.data[i].num >= this.max){
  221. this.context.fillStyle = "rgb(255, 99, 107)";
  222. }else{
  223. this.context.fillStyle = "rgb(179, 191, 209)";
  224. }
  225. this.context.beginPath();
  226. this.context.fillRect(this.left, topLocation, width, barHeight - 5);
  227. this.context.stroke();
  228. let textLocation = 15;
  229. this.context.font = "12px Saira";
  230. this.context.fillStyle = "black";
  231. this.context.fillText(this.data[i].label, textLocation, (this.top) + (i * barHeight) + (barHeight / 1.5));
  232. }
  233. }
  234. }