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 variou 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 defines the 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:

this.canvas = document.getElementById(config.canvasId);

this.data = config.data;

this.color = config.color;

this.barWidth = config.barWidth;

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:

this.maxValue = config.maxValue - Math.floor(config.maxValue % this.gridLineIncrement);

this.minValue = config.minValue;

Step 3

Now we declare the constant:

this.font = "12pt Calibri";

this.axisColor = "#555";

this.gridColor = "black";

this.padding = 10;

Step 4

Now we establish or show the relationship:

this.context = this.canvas.getContext("2d");

this.range = this.maxValue - this.minValue;

this.numGridLines = this.numGridLines = Math.round(this.range / this.gridLineIncrement);

this.longestValueWidth = this.getLongestValueWidth();

this.x = this.padding + this.longestValueWidth;

this.y = this.padding * 2;

this.width = this.canvas.width - (this.longestValueWidth + this.padding * 2);

this.height = this.canvas.height - (this.getLabelAreaHeight() + this.padding * 4);

Step 5

Now we draw the bar chart:

this.drawGridlines();

this.drawYAxis();

this.drawXAxis();

this.drawBars();

this.drawYVAlues();

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:

BarChart.prototype.getLabelAreaHeight = function ()

{

  this.context.font = this.font;

  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.

for (var n = 0; n < this.data.length; n++)

{

   var label = this.data[n].label;

   maxLabelWidth = Math.max(maxLabelWidth, this.context.measureText(label).width);

}

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

return Math.round(maxLabelWidth / Math.sqrt(2));

};

Step 9

Now we will calculate the longestValueWidth:

BarChart.prototype.getLongestValueWidth = function ()

{

   this.context.font = this.font;

   var longestValueWidth = 0;

   for (var n = 0; n <= this.numGridLines; n++)

   {

      var value = this.maxValue - (n * this.gridLineIncrement);

      longestValueWidth = Math.max(longestValueWidth, this.context.measureText(value).width);

   }

   return longestValueWidth;

};

Step 10

In we will put the labels along the x-axis:

BarChart.prototype.drawXLabels = function ()

{

  var context = this.context;

  context.save();

  var data = this.data;

  var barSpacing = this.width / data.length;

 

  for (var n = 0; n < data.length; n++)

   {

       var label = data[n].label;

       context.save();

       context.translate(this.x + ((n + 1 / 2) * barSpacing), this.y + this.height + 10);

       context.rotate(-1 * Math.PI / 4); // rotate 45 degrees

       context.font = this.font;

       context.fillStyle = "black";

       context.textAlign = "right";

       context.textBaseline = "middle";

       context.fillText(label, 0, 0);

       context.restore();

   }

  context.restore();

};

Step 11

In the following we put the values along the y-axis:

BarChart.prototype.drawYVAlues = function ()

{

  var context = this.context;

  context.save();

  context.font = this.font;

  context.fillStyle = "black";

  context.textAlign = "right";

  context.textBaseline = "middle";

 

  for (var n = 0; n <= this.numGridLines; n++)

    {

      var value = this.maxValue - (n * this.gridLineIncrement);

      var thisY = (n * this.height / this.numGridLines) + this.y;

      context.fillText(value, this.x - 5, thisY);

    }

 

  context.restore();

};

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:

var bar = data[n];

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:

context.translate(Math.round(this.x + ((n + 1 / 2) * barSpacing)), Math.round(this.y + this.height));

context.scale(1, -1);

 

Complete code for drawing the bars

 

BarChart.prototype.drawBars = function ()

{

   var context = this.context;

   context.save();

   var data = this.data;

   var barSpacing = this.width / data.length;

   var unitHeight = this.height / this.range;

 

   for (var n = 0; n < data.length; n++)

     {

       var bar = data[n];

       var barHeight = (data[n].value - this.minValue) * unitHeight;

 

       if (barHeight > 0)

        {

          context.save();

          context.translate(Math.round(this.x + ((n + 1 / 2) * barSpacing)), Math.round(this.y + this.height));

          context.scale(1, -1);

 

          context.beginPath();

          context.rect(-this.barWidth / 2, 0, this.barWidth, barHeight);

          context.fillStyle = this.color;

          context.fill();

          context.restore();

        }

    }

   context.restore();

};

Step 13

In the following we draw the grid lines:

BarChart.prototype.drawGridlines = function ()

{

   var context = this.context;

   context.save();

   context.strokeStyle = this.gridColor;

   context.lineWidth = 2;

 

Here we will draw gridlines along y-axis

   for (var n = 0; n < this.numGridLines; n++)

    {

       var y = (n * this.height / this.numGridLines) + this.y;

       context.beginPath();

       context.moveTo(this.x, y);

       context.lineTo(this.x + this.width, y);

       context.stroke();

    }

   context.restore();

};

Step 14

Here we will draw along the x-axis:

BarChart.prototype.drawXAxis = function ()

{

  var context = this.context;

  context.save();

  context.beginPath();

  context.moveTo(this.x, this.y + this.height);

  context.lineTo(this.x + this.width, this.y + this.height);

  context.strokeStyle = this.axisColor;

  context.lineWidth = 2;

  context.stroke();

  context.restore();

};

Step 15

In this step we will draw along y-axis:

BarChart.prototype.drawYAxis = function ()

{

   var context = this.context;

   context.save();

   context.beginPath();

   context.moveTo(this.x, this.y);

   context.lineTo(this.x, this.height + this.y);

   context.strokeStyle = this.axisColor;

   context.lineWidth = 2;

   context.stroke();

   context.restore();

};

Step 16

On Window  onload we will perform the following operation:

window.onload = function ()

{

   var data = [{

       label: "Eating",

       value: 2

   }, {

       label: "Working",

       value: 8

   }, {

       label: "Sleeping",

       value: 8

   }, {

       label: "Playing",

       value: 2

   }, {

       label: "Entertainment",

       value: 4

   }];

 

   new BarChart({

       canvasId: "myCanvas",

       data: data,

       color: "red",

       barWidth: 50,

       minValue: 0,

       maxValue: 10,

       gridLineIncrement: 2

   });

};

Example

<!DOCTYPE html>

 

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">

<head>

    <meta charset="utf-8" />

    <title>Bar Graph in HTML5</title>

    <script>

        function BarChart(config) {

            // user defined properties

            this.canvas = document.getElementById(config.canvasId);

            this.data = config.data;

            this.color = config.color;

            this.barWidth = config.barWidth;

            this.gridLineIncrement = config.gridLineIncrement;

 

            this.maxValue = config.maxValue - Math.floor(config.maxValue % this.gridLineIncrement);

            this.minValue = config.minValue;

 

            // constants

            this.font = "12pt Calibri";

            this.axisColor = "#555";

            this.gridColor = "black";

            this.padding = 10;

 

            // relationships

            this.context = this.canvas.getContext("2d");

            this.range = this.maxValue - this.minValue;

            this.numGridLines = this.numGridLines = Math.round(this.range / this.gridLineIncrement);

            this.longestValueWidth = this.getLongestValueWidth();

            this.x = this.padding + this.longestValueWidth;

            this.y = this.padding * 2;

            this.width = this.canvas.width - (this.longestValueWidth + this.padding * 2);

            this.height = this.canvas.height - (this.getLabelAreaHeight() + this.padding * 4);

 

            // draw bar chart

            this.drawGridlines();

            this.drawYAxis();

            this.drawXAxis();

            this.drawBars();

            this.drawYVAlues();

            this.drawXLabels();

        }

 

        BarChart.prototype.getLabelAreaHeight = function () {

            this.context.font = this.font;

            var maxLabelWidth = 0;

 

            for (var n = 0; n < this.data.length; n++) {

                var label = this.data[n].label;

                maxLabelWidth = Math.max(maxLabelWidth, this.context.measureText(label).width);

            }

 

            return Math.round(maxLabelWidth / Math.sqrt(2));

        };

 

        BarChart.prototype.getLongestValueWidth = function () {

            this.context.font = this.font;

            var longestValueWidth = 0;

            for (var n = 0; n <= this.numGridLines; n++) {

                var value = this.maxValue - (n * this.gridLineIncrement);

                longestValueWidth = Math.max(longestValueWidth, this.context.measureText(value).width);

 

            }

            return longestValueWidth;

        };

 

        BarChart.prototype.drawXLabels = function () {

            var context = this.context;

            context.save();

            var data = this.data;

            var barSpacing = this.width / data.length;

 

            for (var n = 0; n < data.length; n++) {

                var label = data[n].label;

                context.save();

                context.translate(this.x + ((n + 1 / 2) * barSpacing), this.y + this.height + 10);

                context.rotate(-1 * Math.PI / 4); // rotate 45 degrees

                context.font = this.font;

                context.fillStyle = "black";

                context.textAlign = "right";

                context.textBaseline = "middle";

                context.fillText(label, 0, 0);

                context.restore();

            }

            context.restore();

        };

 

        BarChart.prototype.drawYVAlues = function () {

            var context = this.context;

            context.save();

            context.font = this.font;

            context.fillStyle = "black";

            context.textAlign = "right";

            context.textBaseline = "middle";

 

            for (var n = 0; n <= this.numGridLines; n++) {

                var value = this.maxValue - (n * this.gridLineIncrement);

                var thisY = (n * this.height / this.numGridLines) + this.y;

                context.fillText(value, this.x - 5, thisY);

            }

 

            context.restore();

        };

 

        BarChart.prototype.drawBars = function () {

            var context = this.context;

            context.save();

            var data = this.data;

            var barSpacing = this.width / data.length;

            var unitHeight = this.height / this.range;

 

            for (var n = 0; n < data.length; n++) {

                var bar = data[n];

                var barHeight = (data[n].value - this.minValue) * unitHeight;

 

                if (barHeight > 0) {

                    context.save();

                    context.translate(Math.round(this.x + ((n + 1 / 2) * barSpacing)), Math.round(this.y + this.height));

                   

                    context.scale(1, -1);

 

                    context.beginPath();

                    context.rect(-this.barWidth / 2, 0, this.barWidth, barHeight);

                    context.fillStyle = this.color;

                    context.fill();

                    context.restore();

                }

            }

            context.restore();

        };

 

        BarChart.prototype.drawGridlines = function () {

            var context = this.context;

            context.save();

            context.strokeStyle = this.gridColor;

            context.lineWidth = 2;

 

            // draw y axis grid lines

            for (var n = 0; n < this.numGridLines; n++) {

                var y = (n * this.height / this.numGridLines) + this.y;

                context.beginPath();

                context.moveTo(this.x, y);

                context.lineTo(this.x + this.width, y);

                context.stroke();

            }

            context.restore();

        };

 

        BarChart.prototype.drawXAxis = function () {

            var context = this.context;

            context.save();

            context.beginPath();

            context.moveTo(this.x, this.y + this.height);

            context.lineTo(this.x + this.width, this.y + this.height);

            context.strokeStyle = this.axisColor;

            context.lineWidth = 2;

            context.stroke();

            context.restore();

        };

 

        BarChart.prototype.drawYAxis = function () {

            var context = this.context;

            context.save();

            context.beginPath();

            context.moveTo(this.x, this.y);

            context.lineTo(this.x, this.height + this.y);

            context.strokeStyle = this.axisColor;

            context.lineWidth = 2;

            context.stroke();

            context.restore();

        };

 

        window.onload = function () {

            var data = [{

                label: "Eating",

                value: 2

            }, {

                label: "Working",

                value: 8

            }, {

                label: "Sleeping",

                value: 8

            }, {

                label: "Playing",

                value: 2

            }, {

                label: "Entertainment",

                value: 4

            }];

 

            new BarChart({

                canvasId: "myCanvas",

                data: data,

                color: "red",

                barWidth: 50,

                minValue: 0,

                maxValue: 10,

                gridLineIncrement: 2

            });

        };

    </script>

</head>

<body>

    <canvas id="myCanvas" width="600" height="300" style="border: 2px solid black;"></canvas>

</body>

</html>

Output

Output

bargraph.jpg

Up Next
    Ebook Download
    View all
    Learn
    View all