HTML5 Bar Graph Using Canvas

HTML5 Bar Graph using Canvas

 
In this article, I show how to create a simple bar graph on an HTML5 Canvas using JavaScript.
 

Introduction of Bar Graph

 
The bar graph displays a list of rectangles of various heights proportional to the values they represent. The number of bars, their values and labels are all inputs that are defined by us. Or we can say that in bar graphs each sample is plotted as a rectangle scaled to the height or width of the sample.
 

Properties of Bar Graph

 
There are various properties of a Bar Graph:
  • width It defines the total width of the graph.
  • height It defines the total height of the graph.
  • colors It represents the array of colors. (For example: "red","blue", etc.) If the number of bars is greater than the number of colors then the colors will repeat.
  • margin It defines the amount of space required between bars (in pixels)
  • background Color It defines the color of the canvas behind the bars.
  • maxValue It definesthe maximum value. If this value is not defined then the heights of all bar graphs will all be proportional to the largest value of all the current values.
In the example shown below the script tag defines a function named BarChart(). The BarChart() function takes one parameter called "config" that corresponds to the context of the canvas on which to draw the bar graph. In addition to the config parameter, the function contains user-defined properties, variables, constants, and methods.
 
Step 1
 
User defined properties are:
  1. this.canvas = document.getElementById(config.canvasId);  
  2. this.data = config.data;  
  3. this.color = config.color;  
  4. this.barWidth = config.barWidth;  
  5. this.gridLineIncrement = config.gridLineIncrement;   
Step 2
 
Now we will adjust the maximum value to the highest possible value divisible by the grid line increment value and less than the requested maximum value, as in the following:
  1. this.maxValue = config.maxValue - Math.floor(config.maxValue % this.gridLineIncrement);  
  2. this.minValue = config.minValue; 
Step 3
 
Now we declare the constant:
  1. this.font = "12pt Calibri";  
  2. this.axisColor = "#555";  
  3. this.gridColor = "black";  
  4. this.padding = 10
Step 4
 
Now we establish or show the relationship:
  1. thisthis.context = this.canvas.getContext("2d");  
  2. thisthis.range = this.maxValue - this.minValue;  
  3. thisthis.numGridLines = this.numGridLines = Math.round(this.range / this.gridLineIncrement);  
  4. thisthis.longestValueWidth = this.getLongestValueWidth();  
  5. thisthis.x = this.padding + this.longestValueWidth;  
  6. thisthis.y = this.padding * 2;  
  7. thisthis.width = this.canvas.width - (this.longestValueWidth + this.padding * 2);  
  8. thisthis.height = this.canvas.height - (this.getLabelAreaHeight() + this.padding * 4); 
Step 5
 
Now we draw the bar chart:
  1. this.drawGridlines();  
  2. this.drawYAxis();  
  3. this.drawXAxis();  
  4. this.drawBars();  
  5. this.drawYVAlues();  
  6. this.drawXLabels(); 
Step 6
 
Now we will get the label height by finding the maximum label width and use trigonometry to determine the projected height since the text will be rotated by 45 degrees. See the following
  1. BarChart.prototype.getLabelAreaHeight = function ()  
  2. {  
  3.   thisthis.context.font = this.font;  
  4.   var maxLabelWidth = 0
Step 7
 
Now we are going to apply the loop through all labels and determine which label is the longest. Use this information to determine the label width.
  1. for (var n = 0; n < this.data.length; n++)  
  2. {  
  3.    var label = this.data[n].label;  
  4.    maxLabelWidth = Math.max(maxLabelWidth, this.context.measureText(label).width);  
  5. }
Step 8
 
Now we will return the y component of the labelWidth that is at a 45 degree angle
 
             a^2 + b^2 = c^2
 
Where  a = b
 
             c = labelWidth
             a = height component of right triangle solve for a
  1. return Math.round(maxLabelWidth / Math.sqrt(2));  
  2. }; 
Step 9
 
Now we will calculate the longestValueWidth:
  1. BarChart.prototype.getLongestValueWidth = function ()  
  2. {  
  3.    thisthis.context.font = this.font;  
  4.    var longestValueWidth = 0;  
  5.    for (var n = 0; n <= this.numGridLines; n++)  
  6.    {  
  7.       var value = this.maxValue - (n * this.gridLineIncrement);  
  8.       longestValueWidth = Math.max(longestValueWidth, this.context.measureText(value).width);  
  9.    }  
  10.    return longestValueWidth;  
  11. }; 
Step 10
 
In we will put the labels along the x-axis
  1. BarChart.prototype.drawXLabels = function ()  
  2. {  
  3.   var context = this.context;  
  4.   context.save();  
  5.   var data = this.data;  
  6.   var barSpacing = this.width / data.length;  
  7.    
  8.   for (var n = 0; n < data.length; n++)  
  9.    {  
  10.        var label = data[n].label;  
  11.        context.save();  
  12.        context.translate(this.x + ((n + 1 / 2) * barSpacing), this.y + this.height + 10);  
  13.        context.rotate(-1 * Math.PI / 4); // rotate 45 degrees  
  14.        context.font = this.font;  
  15.        context.fillStyle = "black";  
  16.        context.textAlign = "right";  
  17.        context.textBaseline = "middle";  
  18.        context.fillText(label, 0, 0);  
  19.        context.restore();  
  20.    }  
  21.   context.restore();  
  22. }; 
Step 11
 
In the following we put the values along the y-axis
  1. BarChart.prototype.drawYVAlues = function ()  
  2. {  
  3.   var context = this.context;  
  4.   context.save();  
  5.   context.font = this.font;  
  6.   context.fillStyle = "black";  
  7.   context.textAlign = "right";  
  8.   context.textBaseline = "middle";  
  9.    
  10.   for (var n = 0; n <= this.numGridLines; n++)  
  11.     {  
  12.       var value = this.maxValue - (n * this.gridLineIncrement);  
  13.       var thisY = (n * this.height / this.numGridLines) + this.y;  
  14.       context.fillText(value, this.x - 5, thisY);  
  15.     }  
  16.    
  17.   context.restore();  
  18. }; 
Step 12
 
In the following, we draw the bars.
 
If the bar height is less than zero then that means its value is less than the value. Since we don't want to draw bars below the x-axis, only draw bars whose height is greater than zero, as in the following 2 lines of code
  1. var bar = data[n];  
  2. var barHeight = (data[n].value - this.minValue) * unitHeight; 
For our convenience, we can draw the bars upsidedown starting at the x-axis and then flip them back into the correct orientation using "scale(1, -1)". This is a great example of how transformations can help reduce computations as in the following two lines of code:
  1. context.translate(Math.round(this.x + ((n + 1 / 2) * barSpacing)), Math.round(this.y + this.height));  
  2. context.scale(1, -1); 
Complete code for drawing the bars
  1. BarChart.prototype.drawBars = function ()  
  2. {  
  3.    var context = this.context;  
  4.    context.save();  
  5.    var data = this.data;  
  6.    var barSpacing = this.width / data.length;  
  7.    var unitHeight = this.height / this.range;  
  8.    
  9.    for (var n = 0; n < data.length; n++)  
  10.      {  
  11.        var bar = data[n];  
  12.        var barHeight = (data[n].value - this.minValue) * unitHeight;  
  13.    
  14.        if (barHeight > 0)  
  15.         {  
  16.           context.save();  
  17.           context.translate(Math.round(this.x + ((n + 1 / 2) * barSpacing)), Math.round(this.y + this.height));  
  18.           context.scale(1, -1);  
  19.    
  20.           context.beginPath();  
  21.           context.rect(-this.barWidth / 2, 0, this.barWidth, barHeight);  
  22.           context.fillStyle = this.color;  
  23.           context.fill();  
  24.           context.restore();  
  25.         }  
  26.     }  
  27.    context.restore();  
  28. }; 
Step 13
 
In the following we draw the grid lines:
  1. BarChart.prototype.drawGridlines = function ()  
  2. {  
  3.    var context = this.context;  
  4.    context.save();  
  5.    context.strokeStyle = this.gridColor;  
  6.    context.lineWidth = 2;  
  7.    
  8. Here we will draw gridlines along y-axis  
  9.    for (var n = 0; n < this.numGridLines; n++)  
  10.     {  
  11.        var y = (n * this.height / this.numGridLines) + this.y;  
  12.        context.beginPath();  
  13.        context.moveTo(this.x, y);  
  14.        context.lineTo(this.x + this.width, y);  
  15.        context.stroke();  
  16.     }  
  17.    context.restore();  
  18. }; 
Step 14
 
Here we will draw along the x-axis:
  1. BarChart.prototype.drawXAxis = function ()  
  2. {  
  3.   var context = this.context;  
  4.   context.save();  
  5.   context.beginPath();  
  6.   context.moveTo(this.x, this.y + this.height);  
  7.   context.lineTo(this.x + this.width, this.y + this.height);  
  8.   context.strokeStyle = this.axisColor;  
  9.   context.lineWidth = 2;  
  10.   context.stroke();  
  11.   context.restore();  
  12. }; 
Step 15
 
In this step we will draw along y-axis
  1. BarChart.prototype.drawYAxis = function ()  
  2. {  
  3.    var context = this.context;  
  4.    context.save();  
  5.    context.beginPath();  
  6.    context.moveTo(this.x, this.y);  
  7.    context.lineTo(this.x, this.height + this.y);  
  8.    context.strokeStyle = this.axisColor;  
  9.    context.lineWidth = 2;  
  10.    context.stroke();  
  11.    context.restore();  
  12. }; 
Step 16
 
On Window  onload we will perform the following operation:
  1. window.onload = function ()  
  2. {  
  3.    var data = [{  
  4.        label: "Eating",  
  5.        value: 2  
  6.    }, {  
  7.        label: "Working",  
  8.        value: 8  
  9.    }, {  
  10.        label: "Sleeping",  
  11.        value: 8  
  12.    }, {  
  13.        label: "Playing",  
  14.        value: 2  
  15.    }, {  
  16.        label: "Entertainment",  
  17.        value: 4  
  18.    }];  
  19.    
  20.    new BarChart({  
  21.        canvasId: "myCanvas",  
  22.        data: data,  
  23.        color: "red",  
  24.        barWidth: 50,  
  25.        minValue: 0,  
  26.        maxValue: 10,  
  27.        gridLineIncrement: 2  
  28.    });  
  29. }; 
Example
  1. <!DOCTYPE html>  
  2.    
  3. <html lang="en" xmlns="http://www.w3.org/1999/xhtml">  
  4. <head>  
  5.     <meta charset="utf-8" />  
  6.     <title>Bar Graph in HTML5</title>  
  7.     <script>  
  8.         function BarChart(config) {  
  9.             // user defined properties  
  10.             this.canvas = document.getElementById(config.canvasId);  
  11.             this.data = config.data;  
  12.             this.color = config.color;  
  13.             this.barWidth = config.barWidth;  
  14.             this.gridLineIncrement = config.gridLineIncrement;  
  15.    
  16.             this.maxValue = config.maxValue - Math.floor(config.maxValue % this.gridLineIncrement);  
  17.             this.minValue = config.minValue;  
  18.    
  19.             // constants  
  20.             this.font = "12pt Calibri";  
  21.             this.axisColor = "#555";  
  22.             this.gridColor = "black";  
  23.             this.padding = 10;  
  24.    
  25.             // relationships  
  26.             thisthis.context = this.canvas.getContext("2d");  
  27.             thisthis.range = this.maxValue - this.minValue;  
  28.             thisthis.numGridLines = this.numGridLines = Math.round(this.range / this.gridLineIncrement);  
  29.             thisthis.longestValueWidth = this.getLongestValueWidth();  
  30.             thisthis.x = this.padding + this.longestValueWidth;  
  31.             thisthis.y = this.padding * 2;  
  32.             thisthis.width = this.canvas.width - (this.longestValueWidth + this.padding * 2);  
  33.             thisthis.height = this.canvas.height - (this.getLabelAreaHeight() + this.padding * 4);  
  34.    
  35.             // draw bar chart  
  36.             this.drawGridlines();  
  37.             this.drawYAxis();  
  38.             this.drawXAxis();  
  39.             this.drawBars();  
  40.             this.drawYVAlues();  
  41.             this.drawXLabels();  
  42.         }  
  43.    
  44.         BarChart.prototype.getLabelAreaHeight = function () {  
  45.             thisthis.context.font = this.font;  
  46.             var maxLabelWidth = 0;  
  47.    
  48.             for (var n = 0; n < this.data.length; n++) {  
  49.                 var label = this.data[n].label;  
  50.                 maxLabelWidth = Math.max(maxLabelWidth, this.context.measureText(label).width);  
  51.             }  
  52.    
  53.             return Math.round(maxLabelWidth / Math.sqrt(2));  
  54.         };  
  55.    
  56.         BarChart.prototype.getLongestValueWidth = function () {  
  57.             thisthis.context.font = this.font;  
  58.             var longestValueWidth = 0;  
  59.             for (var n = 0; n <= this.numGridLines; n++) {  
  60.                 var value = this.maxValue - (n * this.gridLineIncrement);  
  61.                 longestValueWidth = Math.max(longestValueWidth, this.context.measureText(value).width);  
  62.    
  63.             }  
  64.             return longestValueWidth;  
  65.         };  
  66.    
  67.         BarChart.prototype.drawXLabels = function () {  
  68.             var context = this.context;  
  69.             context.save();  
  70.             var data = this.data;  
  71.             var barSpacing = this.width / data.length;  
  72.    
  73.             for (var n = 0; n < data.length; n++) {  
  74.                 var label = data[n].label;  
  75.                 context.save();  
  76.                 context.translate(this.x + ((n + 1 / 2) * barSpacing), this.y + this.height + 10);  
  77.                 context.rotate(-1 * Math.PI / 4); // rotate 45 degrees  
  78.                 context.font = this.font;  
  79.                 context.fillStyle = "black";  
  80.                 context.textAlign = "right";  
  81.                 context.textBaseline = "middle";  
  82.                 context.fillText(label, 0, 0);  
  83.                 context.restore();  
  84.             }  
  85.             context.restore();  
  86.         };  
  87.    
  88.         BarChart.prototype.drawYVAlues = function () {  
  89.             var context = this.context;  
  90.             context.save();  
  91.             context.font = this.font;  
  92.             context.fillStyle = "black";  
  93.             context.textAlign = "right";  
  94.             context.textBaseline = "middle";  
  95.    
  96.             for (var n = 0; n <= this.numGridLines; n++) {  
  97.                 var value = this.maxValue - (n * this.gridLineIncrement);  
  98.                 var thisY = (n * this.height / this.numGridLines) + this.y;  
  99.                 context.fillText(value, this.x - 5, thisY);  
  100.             }  
  101.    
  102.             context.restore();  
  103.         };  
  104.    
  105.         BarChart.prototype.drawBars = function () {  
  106.             var context = this.context;  
  107.             context.save();  
  108.             var data = this.data;  
  109.             var barSpacing = this.width / data.length;  
  110.             var unitHeight = this.height / this.range;  
  111.    
  112.             for (var n = 0; n < data.length; n++) {  
  113.                 var bar = data[n];  
  114.                 var barHeight = (data[n].value - this.minValue) * unitHeight;  
  115.    
  116.                 if (barHeight > 0) {  
  117.                     context.save();  
  118.                     context.translate(Math.round(this.x + ((n + 1 / 2) * barSpacing)), Math.round(this.y + this.height));  
  119.                      
  120.                     context.scale(1, -1);  
  121.    
  122.                     context.beginPath();  
  123.                     context.rect(-this.barWidth / 2, 0, this.barWidth, barHeight);  
  124.                     context.fillStyle = this.color;  
  125.                     context.fill();  
  126.                     context.restore();  
  127.                 }  
  128.             }  
  129.             context.restore();  
  130.         };  
  131.    
  132.         BarChart.prototype.drawGridlines = function () {  
  133.             var context = this.context;  
  134.             context.save();  
  135.             context.strokeStyle = this.gridColor;  
  136.             context.lineWidth = 2;  
  137.    
  138.             // draw y axis grid lines  
  139.             for (var n = 0; n < this.numGridLines; n++) {  
  140.                 var y = (n * this.height / this.numGridLines) + this.y;  
  141.                 context.beginPath();  
  142.                 context.moveTo(this.x, y);  
  143.                 context.lineTo(this.x + this.width, y);  
  144.                 context.stroke();  
  145.             }  
  146.             context.restore();  
  147.         };  
  148.    
  149.         BarChart.prototype.drawXAxis = function () {  
  150.             var context = this.context;  
  151.             context.save();  
  152.             context.beginPath();  
  153.             context.moveTo(this.x, this.y + this.height);  
  154.             context.lineTo(this.x + this.width, this.y + this.height);  
  155.             context.strokeStyle = this.axisColor;  
  156.             context.lineWidth = 2;  
  157.             context.stroke();  
  158.             context.restore();  
  159.         };  
  160.    
  161.         BarChart.prototype.drawYAxis = function () {  
  162.             var context = this.context;  
  163.             context.save();  
  164.             context.beginPath();  
  165.             context.moveTo(this.x, this.y);  
  166.             context.lineTo(this.x, this.height + this.y);  
  167.             context.strokeStyle = this.axisColor;  
  168.             context.lineWidth = 2;  
  169.             context.stroke();  
  170.             context.restore();  
  171.         };  
  172.    
  173.         window.onload = function () {  
  174.             var data = [{  
  175.                 label: "Eating",  
  176.                 value: 2  
  177.             }, {  
  178.                 label: "Working",  
  179.                 value: 8  
  180.             }, {  
  181.                 label: "Sleeping",  
  182.                 value: 8  
  183.             }, {  
  184.                 label: "Playing",  
  185.                 value: 2  
  186.             }, {  
  187.                 label: "Entertainment",  
  188.                 value: 4  
  189.             }];  
  190.    
  191.             new BarChart({  
  192.                 canvasId: "myCanvas",  
  193.                 data: data,  
  194.                 color: "red",  
  195.                 barWidth: 50,  
  196.                 minValue: 0,  
  197.                 maxValue: 10,  
  198.                 gridLineIncrement: 2  
  199.             });  
  200.         };  
  201.     </script>  
  202. </head>  
  203. <body>  
  204.     <canvas id="myCanvas" width="600" height="300" style="border: 2px solid black;"></canvas>  
  205. </body>  
  206. </html> 
Output
 
bargraph.jpg