| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- //Creates a line graph within a canvas
- //Will expand or shrink to the size of the canvas
- //Inputs:
- // canvas = the canvas that you would like to draw on
- // yName = a string for the name of the Y axis
- // xName = a string for the name of the X axis
- class LineGraph{
- constructor(canvas){
- this.canvas = canvas;
- this.context = canvas.getContext("2d");
- this.left = canvas.clientWidth - (canvas.clientWidth * 0.95);
- this.right = canvas.clientWidth * 1;
- this.top = canvas.clientHeight - (canvas.clientHeight * 1);
- this.bottom = canvas.clientHeight * 0.85;
- this.data = [];
- this.max = 0;
- this.xRange = [];
- this.colors = [];
- this.colorIndex = 0;
- for(let i = 0; i < 100; i++){
- let redRand = Math.floor(Math.random() * 200);
- let greenRand = Math.floor(Math.random() * 200);
- let blueRand = Math.floor(Math.random() * 200);
- this.colors.push(`rgb(${redRand}, ${greenRand}, ${blueRand})`);
- }
- }
- //Add a dataset to the graph to draw
- //Inputs:
- // data = array containing list of numbers as the data points for the graph
- // data[0] will be on the left. data[data.length-1] will be on the right.
- // xRange = array containing two elements, start and end for x axis data (currently only dates)
- // name = string name for the line. Used for display and finding lines. Each must be unique
- addData(data, xRange, name){
- data = {
- set: data,
- colorIndex: this.colorIndex,
- name: name
- }
- this.colorIndex++;
- this.data.push(data);
- let isChange = false;
- for(let point of data.set){
- if(point > this.max){
- this.max = point;
- this.verticalMultiplier = (this.bottom - this.top) / this.max;
- this.horizontalMultiplier = (this.right - this.left) / (data.set.length - 1);
- isChange = true;
- }
- }
- if(this.xRange.length === 0){
- this.xRange = xRange;
- isChange = true;
- }else{
- if(xRange[0] < this.xRange[0]){
- this.xRange[0] = xRange[0];
- isChange = true;
- }
- if(xRange[1] > this.xRange[1]){
- this.xRange[1] = xRange[1];
- isChange = true;
- }
- }
- if(isChange){
- this.drawGraph();
- }else{
- this.drawLine(data);
- }
- }
- //Removes a single data set from the graph and its line
- //Inputs:
- // id = the unique identifier of the data set that was passed in with addData function
- removeData(name){
- for(let i = 0; i < this.data.length; i++){
- if(this.data[i].name === name){
- this.data.splice(i, 1);
- break;
- }
- }
- this.drawGraph();
- }
- //Completely clears all data
- //Does not delete the current graph displaying
- clearData(){
- this.max = 0;
- this.data = [];
- this.xRange = [];
- }
- addTitle(title){
- this.top = this.canvas.clientHeight - (this.canvas.clientHeight * 0.9);
-
- this.title = title;
- }
- /**********
- *********PRIVATE*********
- **********/
- drawGraph(){
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
-
- this.drawYAxis();
- this.drawXAxis();
- for(let dataSet of this.data){
- this.drawLine(dataSet);
- }
- if(this.title){
- this.context.font = "25px Saira";
- let xLocation = ((this.right - this.left) / 2) - (this.context.measureText(this.title).width / 2);
- this.context.fillText(this.title, xLocation, this.top - 10);
- }
- }
- drawLine(data){
- for(let i = 0; i < data.set.length - 1; i++){
- this.context.beginPath();
- this.context.moveTo(this.left + (this.horizontalMultiplier * i), this.bottom - (this.verticalMultiplier * data.set[i]));
- this.context.lineTo(this.left + (this.horizontalMultiplier * (i + 1)), this.bottom - (this.verticalMultiplier * data.set[i + 1]));
- this.context.strokeStyle = this.colors[data.colorIndex];
- this.context.lineWidth = 2;
- this.context.stroke();
- }
- this.context.strokeStyle = "black";
- if(this.data.length > 1){
- this.drawLegend(data.colorIndex, data.name);
- }
- }
- drawXAxis(){
- this.context.beginPath();
- this.context.moveTo(this.left, this.bottom);
- this.context.lineTo(this.right, this.bottom);
- this.context.lineWidth = 4;
- this.context.stroke();
- this.context.setLineDash([5, 10]);
- this.context.font = "10px Arial";
- this.context.lineWidth = 1;
- if(Object.prototype.toString.call(this.xRange[0]) === '[object Date]'){
- 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;
- let showDate = new Date(this.xRange[0]);
-
- for(let i = 0; i < diff; i += Math.floor(diff / 10)){
- this.context.fillText(showDate.toLocaleDateString("en-US", {month: "short", day: "numeric", year: "2-digit"}), this.left + (this.horizontalMultiplier * i) - 20, this.bottom + 15);
- if(i !== 0){
- this.context.beginPath()
- this.context.moveTo(this.left + (this.horizontalMultiplier * i), this.bottom);
- this.context.lineTo(this.left + (this.horizontalMultiplier * i), this.top);
- this.context.strokeStyle = "#a5a5a5";
- this.context.stroke();
- }
- showDate.setDate(showDate.getDate() + Math.abs(diff / 10));
- }
-
- }
- this.context.strokeStyle = "black";
- this.context.setLineDash([]);
- }
- drawYAxis(){
- this.context.beginPath();
- this.context.moveTo(this.left, this.top);
- this.context.lineTo(this.left, this.bottom);
- this.context.lineWidth = 2;
- this.context.stroke();
- this.context.setLineDash([5, 10]);
- this.context.font = "10px Arial";
- this.context.lineWidth = 1;
- let axisNum = 0;
- let verticalIncrement = (this.bottom - this.top) / 10;
- let verticalOffset = 0;
- do{
- this.context.fillText(Math.round(axisNum).toString(), this.left - 20, this.bottom - verticalOffset + 3);
- this.context.beginPath();
- this.context.moveTo(this.left, this.bottom - verticalOffset);
- this.context.lineTo(this.right, this.bottom - verticalOffset);
- this.context.strokeStyle = "#a5a5a5";
- this.context.stroke();
- verticalOffset += verticalIncrement;
- axisNum += this.max / 10;
- }while(verticalOffset <= (this.bottom - this.top));
- this.context.strokeStyle = "black";
- this.context.setLineDash([]);
- }
- drawLegend(colorIndex, name){
- let verticalOffset;
- for(let i = 0; i < this.data.length; i++){
- if(this.data[i].name === name){
- verticalOffset = i * 25;
- break;
- }
- }
- this.context.beginPath();
- this.context.fillStyle = this.colors[colorIndex];
- this.context.fillRect(this.right + 50, this.top + 50 + verticalOffset, 10, 10);
- this.context.stroke();
- this.context.font = "15px Arial";
- this.context.fillText(name, this.right + 65, this.top + 60 + verticalOffset);
- this.context.fillStyle = "black";
- }
- }
- class HorizontalBarGraph{
- constructor(canvas){
- this.canvas = canvas;
- this.context = canvas.getContext("2d");
- this.left = 0;
- this.right = canvas.clientWidth;
- this.top = canvas.clientHeight - (canvas.clientHeight * 0.99);
- this.bottom = canvas.clientHeight;
- this.data = [];
- this.max = 0;
- }
- //Adds an array of data points to the chart
- //All data is removed and redrawn when called
- //Must pass in all data points
- //Inputs:
- // dataArray: array of objects
- // num: number for the actual data
- // label: text to display on bar
- addData(dataArray){
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- for(let point of dataArray){
- if(point.num > this.max){
- this.max = point.num;
- }
- this.data.push(point);
- }
- this.drawGraph();
- }
- drawGraph(){
- let barHeight = ((this.bottom - this.top) / this.data.length) - 2;
- for(let i = 0; i < this.data.length; i++){
- let topLocation = this.top + (i * barHeight) + 5;
- let width = (this.right - this.left) * (this.data[i].num / this.max);
- if(this.data[i].num >= this.max){
- this.context.fillStyle = "rgb(255, 99, 107)";
- }else{
- this.context.fillStyle = "rgb(179, 191, 209)";
- }
- this.context.beginPath();
- this.context.fillRect(this.left, topLocation, width, barHeight - 5);
- this.context.stroke();
- let textLocation = 15;
- this.context.font = "12px Saira";
- this.context.fillStyle = "black";
- this.context.fillText(this.data[i].label, textLocation, (this.top) + (i * barHeight) + (barHeight / 1.5));
- }
- }
- }
- module.exports = {
- LineGraph: LineGraph,
- HorizontalBarGraph: HorizontalBarGraph
- }
|