Graphing Equation in HTML5 Using Canvas

Introduction

 
In this article, I will walk through how to use a graphing equation in an HTML5 canvas.
 

Graphing Equation in HTML5 using Canvas

 

What is the Graph of an Equation?

 
It is the set of points where the equation is true.
 

Features of a Graph

 
For a graph to be "complete" you need to show all the important features.
  • Peaks
  • Valleys
  • Flat Areas
  • Asymptotes
  • Any other special feature
Browser Support
 
It is supported by all major browsers such as Internet Explorer 9, Firefox 3.6+, Safari 4+ and Chrome, etc.
 
Procedure for creating the graphing equations
 
Step 1
 
We first define the element using a "canvas" in HTML5. The height and width attributes set the canvas and graph size.
 
<canvas id="myCanvas" width="600" height="300" style="border: 1px solid black;"></canvas>
 
Step 2
 
In order to interact with this canvas through JavaScript, we will need to first get the element by Id and then create a context. 
  1. <script type="text/javascript">  
  2.     var canvas = document.getElementById('mycanvas');  
  3.     var ctx = canvas.getContext("2d");  
  4. </script> 
Step 3
 
In the following code we will create a "Graph" function in which we define various methods, variables, constants and properties.
  1. function Graph(config)  
  2. {  
  3.    // user defined properties  
  4.    this.canvas = document.getElementById(config.canvasId);  
  5.    this.minX = config.minX;  
  6.    this.minY = config.minY;  
  7.    this.maxX = config.maxX;  
  8.    this.maxY = config.maxY;  
  9.    this.unitsPerTick = config.unitsPerTick;  
  10.    
  11.    // constants  
  12.    this.axisColor = "#aaa";  
  13.    this.font = "8pt Calibri";  
  14.    this.tickSize = 20;  
  15.    
  16.    // relationships  
  17.    this.context = this.canvas.getContext("2d");  
  18.    this.rangeX = this.maxX - this.minX;  
  19.    this.rangeY = this.maxY - this.minY;  
  20.    this.unitX = this.canvas.width / this.rangeX;  
  21.    this.unitY = this.canvas.height / this.rangeY;  
  22.    this.centerY = Math.round(Math.abs(this.minY / this.rangeY) * this.canvas.height);  
  23.    this.centerX = Math.round(Math.abs(this.minX / this.rangeX) * this.canvas.width);  
  24.    this.iteration = (this.maxX - this.minX) / 1000;  
  25.    this.scaleX = this.canvas.width / this.rangeX;  
  26.    this.scaleY = this.canvas.height / this.rangeY;  
  27.    
  28.    // draw x and y axis  
  29.    this.drawXAxis();  
  30.    this.drawYAxis();  
Step 4
 
In the following code, we will draw the x-axis. On the x-axis, we draw the left tick marks and then draw the right tick marks. For drawing both of the tick marks we apply the loop.
  1. Graph.prototype.drawXAxis = function ()  
  2. {  
  3.    var context = this.context;  
  4.    context.save();  
  5.    context.beginPath();  
  6.    context.moveTo(0, this.centerY);  
  7.    context.lineTo(this.canvas.width, this.centerY);  
  8.    context.strokeStyle = this.axisColor;  
  9.    context.lineWidth = 2;  
  10.    context.stroke(); 
Figure 1
 
The following figure represents the x-axis:
 
graph1.jpg
 
  1. // draw tick marks  
  2. var xPosIncrement = this.unitsPerTick * this.unitX;  
  3. var xPos, unit;  
  4. context.font = this.font;  
  5. context.textAlign = "center";  
  6. context.textBaseline = "top";  
  7.   
  8. // draw left tick marks  
  9. xPos = this.centerX - xPosIncrement;  
  10. unit = -1 * this.unitsPerTick;  
  11. while (xPos > 0)  
  12.    {  
  13.       context.moveTo(xPos, this.centerY - this.tickSize / 2);  
  14.       context.lineTo(xPos, this.centerY + this.tickSize / 2);  
  15.       context.stroke();  
  16.       context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);  
  17.       unit -= this.unitsPerTick;  
  18.       xPos = Math.round(xPos - xPosIncrement);  
  19.    } 
Figure 2
 
The following figure represents the left side tick mark of the x-axis:
 
lgraph.jpg
 
  1. // draw right tick marks  
  2. xPos = this.centerX + xPosIncrement;  
  3. unit = this.unitsPerTick;  
  4. while (xPos < this.canvas.width)  
  5.    {  
  6.       context.moveTo(xPos, this.centerY - this.tickSize / 2);  
  7.       context.lineTo(xPos, this.centerY + this.tickSize / 2);  
  8.       context.stroke();  
  9.       context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);  
  10.       unit += this.unitsPerTick;  
  11.       xPos = Math.round(xPos + xPosIncrement);  
  12.    }  
  13. context.restore(); 
Figure 3
 
The following figure represents the right side tick mark of the x-axis
 
rigraph.jpg
 
Step 5
 
Similarily, we will draw the y-axis. On the y-axis we will draw the tick marks along the upper side of the y-axis and finally, we draw the tick mark for the bottom side of the y-axis. For drawing both of the tick marks and labels we apply the loop. 
  1. Graph.prototype.drawYAxis = function ()  
  2. {  
  3.    var context = this.context;  
  4.    context.save();  
  5.    context.beginPath();  
  6.    context.moveTo(this.centerX, 0);  
  7.    context.lineTo(this.centerX, this.canvas.height);  
  8.    context.strokeStyle = this.axisColor;  
  9.    context.lineWidth = 2;  
  10.    context.stroke(); 
Figure 4
 
The following figure represents the y-axis
 
line.jpg
 
  1. // draw tick marks   
  2. var yPosIncrement = this.unitsPerTick * this.unitY;  
  3. var yPos, unit;  
  4. context.font = this.font;  
  5. context.textAlign = "right";  
  6. context.textBaseline = "middle";  
  7.   
  8. // draw top tick marks  
  9. yPos = this.centerY - yPosIncrement;  
  10. unit = this.unitsPerTick;  
  11. while (yPos > 0)  
  12.   {  
  13.      context.moveTo(this.centerX - this.tickSize / 2, yPos);  
  14.      context.lineTo(this.centerX + this.tickSize / 2, yPos);  
  15.      context.stroke();  
  16.      context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);  
  17.      unit += this.unitsPerTick;  
  18.      yPos = Math.round(yPos - yPosIncrement);  
  19.    } 
Figure 5
 
The following figure represents the upper side tick mark on the y-axis
 
ugraph.jpg
 
  1. // draw bottom tick marks  
  2. yPos = this.centerY + yPosIncrement;  
  3. unit = -1 * this.unitsPerTick;  
  4. while (yPos < this.canvas.height)  
  5.   {  
  6.     context.moveTo(this.centerX - this.tickSize / 2, yPos);  
  7.     context.lineTo(this.centerX + this.tickSize / 2, yPos);  
  8.     context.stroke();  
  9.     context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);  
  10.     unit -= this.unitsPerTick;  
  11.     yPos = Math.round(yPos + yPosIncrement);  
  12.   }  
  13. context.restore(); 
Figure 6
 
The following figure represents the lower side of the y-axis
 
lograph.jpg
 
Figure 7
 
The following figure represents the combination of the x-axis and the y-axis. Both form a graph.
 
graph3.jpg
 
Step 6
 
In the following step we will draw the equation on the graph. We will apply the loop for drawing the equation.
  1. Graph.prototype.drawEquation = function (equation, color, thickness)  
  2. {  
  3.    var context = this.context;  
  4.    context.save();  
  5.    context.save();  
  6.    this.transformContext();  
  7.    
  8.    context.beginPath();  
  9.    context.moveTo(this.minX, equation(this.minX));  
  10.    
  11.    for (var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration)  
  12.     {  
  13.        context.lineTo(x, equation(x));  
  14.     }  
  15.    
  16.     context.restore();  
  17.     context.lineJoin = "round";  
  18.     context.lineWidth = thickness;  
  19.     context.strokeStyle = color;  
  20.     context.stroke();  
  21.     context.restore();  
  22. }; 
Step 7
 
We will transform the context to move it to the center. Then we will stretch the grid to fit the canvas window, and invert the y scale so that it increments as you move upwards.
  1. Graph.prototype.transformContext = function ()  
  2. {  
  3.    var context = this.context;  
  4.    
  5.    // move context to center of canvas  
  6.    this.context.translate(this.centerX, this.centerY);  
  7.    
  8.    context.scale(this.scaleX, -this.scaleY);  
  9. }; 
Step 8
 
In the following code the window onload we will draw lines on the graph.
  1. window.onload = function ()  
  2. {  
  3.    var myGraph = new Graph({  
  4.        canvasId: "myCanvas",  
  5.        minX: -10,  
  6.        minY: -10,  
  7.        maxX: 10,  
  8.        maxY: 10,  
  9.        unitsPerTick: 1  
  10.     });  
  11.    
  12.     myGraph.drawEquation(function (x) {  
  13.          return 5 * Math.sin(x);  
  14.     }, "green", 3);  
  15.    
  16.     myGraph.drawEquation(function (x) {  
  17.          return x * x;  
  18.     }, "blue", 3);  
  19.    
  20.     myGraph.drawEquation(function (x) {  
  21.           return 1 * x;  
  22.     }, "red", 3);  
  23. }; 
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>Graphing Equation in HTML5</title>  
  7.     <script>  
  8.         function Graph(con) {  
  9.             // user defined properties  
  10.             this.canvas = document.getElementById(con.canvasId);  
  11.             this.minX = con.minX;  
  12.             this.minY = con.minY;  
  13.             this.maxX = con.maxX;  
  14.             this.maxY = con.maxY;  
  15.             this.unitsPerTick = con.unitsPerTick;  
  16.    
  17.             // constants  
  18.             this.axisColor = "#aaa";  
  19.             this.font = "8pt Calibri";  
  20.             this.tickSize = 20;  
  21.    
  22.             // relationships  
  23.             this.context = this.canvas.getContext("2d");  
  24.             this.rangeX = this.maxX - this.minX;  
  25.             this.rangeY = this.maxY - this.minY;  
  26.             this.unitX = this.canvas.width / this.rangeX;  
  27.             this.unitY = this.canvas.height / this.rangeY;  
  28.             this.centerY = Math.round(Math.abs(this.minY / this.rangeY) * this.canvas.height);  
  29.             this.centerX = Math.round(Math.abs(this.minX / this.rangeX) * this.canvas.width);  
  30.             this.iteration = (this.maxX - this.minX) / 1000;  
  31.             this.scaleX = this.canvas.width / this.rangeX;  
  32.             this.scaleY = this.canvas.height / this.rangeY;  
  33.    
  34.             // draw x and y axis  
  35.             this.drawXAxis();  
  36.             this.drawYAxis();  
  37.         }  
  38.    
  39.         Graph.prototype.drawXAxis = function () {  
  40.             var context = this.context;  
  41.             context.save();  
  42.             context.beginPath();  
  43.             context.moveTo(0, this.centerY);  
  44.             context.lineTo(this.canvas.width, this.centerY);  
  45.             context.strokeStyle = this.axisColor;  
  46.             context.lineWidth = 2;  
  47.             context.stroke();  
  48.    
  49.             // draw tick marks  
  50.             var xPosIncrement = this.unitsPerTick * this.unitX;  
  51.             var xPos, unit;  
  52.             context.font = this.font;  
  53.             context.textAlign = "center";  
  54.             context.textBaseline = "top";  
  55.    
  56.             // draw left tick marks  
  57.             xPos = this.centerX - xPosIncrement;  
  58.             unit = -1 * this.unitsPerTick;  
  59.             while (xPos > 0) {  
  60.                 context.moveTo(xPos, this.centerY - this.tickSize / 2);  
  61.                 context.lineTo(xPos, this.centerY + this.tickSize / 2);  
  62.                 context.stroke();  
  63.                 context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);  
  64.                 unit -= this.unitsPerTick;  
  65.                 xPos = Math.round(xPos - xPosIncrement);  
  66.             }  
  67.    
  68.             // draw right tick marks  
  69.             xPos = this.centerX + xPosIncrement;  
  70.             unit = this.unitsPerTick;  
  71.             while (xPos < this.canvas.width) {  
  72.                 context.moveTo(xPos, this.centerY - this.tickSize / 2);  
  73.                 context.lineTo(xPos, this.centerY + this.tickSize / 2);  
  74.                 context.stroke();  
  75.                 context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);  
  76.                 unit += this.unitsPerTick;  
  77.                 xPos = Math.round(xPos + xPosIncrement);  
  78.             }  
  79.             context.restore();  
  80.         };  
  81.    
  82.         Graph.prototype.drawYAxis = function () {  
  83.             var context = this.context;  
  84.             context.save();  
  85.             context.beginPath();  
  86.             context.moveTo(this.centerX, 0);  
  87.             context.lineTo(this.centerX, this.canvas.height);  
  88.             context.strokeStyle = this.axisColor;  
  89.             context.lineWidth = 2;  
  90.             context.stroke();  
  91.    
  92.             // draw tick marks   
  93.             var yPosIncrement = this.unitsPerTick * this.unitY;  
  94.             var yPos, unit;  
  95.             context.font = this.font;  
  96.             context.textAlign = "right";  
  97.             context.textBaseline = "middle";  
  98.    
  99.             // draw top tick marks  
  100.             yPos = this.centerY - yPosIncrement;  
  101.             unit = this.unitsPerTick;  
  102.             while (yPos > 0) {  
  103.                 context.moveTo(this.centerX - this.tickSize / 2, yPos);  
  104.                 context.lineTo(this.centerX + this.tickSize / 2, yPos);  
  105.                 context.stroke();  
  106.                 context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);  
  107.                 unit += this.unitsPerTick;  
  108.                 yPos = Math.round(yPos - yPosIncrement);  
  109.             }  
  110.    
  111.             // draw bottom tick marks  
  112.             yPos = this.centerY + yPosIncrement;  
  113.             unit = -1 * this.unitsPerTick;  
  114.             while (yPos < this.canvas.height) {  
  115.                 context.moveTo(this.centerX - this.tickSize / 2, yPos);  
  116.                 context.lineTo(this.centerX + this.tickSize / 2, yPos);  
  117.                 context.stroke();  
  118.                 context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);  
  119.                 unit -= this.unitsPerTick;  
  120.                 yPos = Math.round(yPos + yPosIncrement);  
  121.             }  
  122.             context.restore();  
  123.         };  
  124.    
  125.         Graph.prototype.drawEquation = function (equation, color, thickness) {  
  126.             var context = this.context;  
  127.             context.save();  
  128.             context.save();  
  129.             this.transformContext();  
  130.    
  131.             context.beginPath();  
  132.             context.moveTo(this.minX, equation(this.minX));  
  133.    
  134.             for (var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {  
  135.                 context.lineTo(x, equation(x));  
  136.             }  
  137.    
  138.             context.restore();  
  139.             context.lineJoin = "round";  
  140.             context.lineWidth = thickness;  
  141.             context.strokeStyle = color;  
  142.             context.stroke();  
  143.             context.restore();  
  144.         };  
  145.    
  146.         Graph.prototype.transformContext = function () {  
  147.             var context = this.context;  
  148.    
  149.             // move context to center of canvas  
  150.             this.context.translate(this.centerX, this.centerY);  
  151.    
  152.             context.scale(this.scaleX, -this.scaleY);  
  153.         };  
  154.    
  155.         window.onload = function () {  
  156.             var myGraph = new Graph({  
  157.                 canvasId: "myCanvas",  
  158.                 minX: -10,  
  159.                 minY: -10,  
  160.                 maxX: 10,  
  161.                 maxY: 10,  
  162.                 unitsPerTick: 1  
  163.             });  
  164.    
  165.             myGraph.drawEquation(function (x) {  
  166.                 return 5 * Math.sin(x);  
  167.             }, "green", 3);  
  168.    
  169.             myGraph.drawEquation(function (x) {  
  170.                 return x * x;  
  171.             }, "blue", 3);  
  172.    
  173.             myGraph.drawEquation(function (x) {  
  174.                 return 1 * x;  
  175.             }, "red", 3);  
  176.         };  
  177.     </script>  
  178. </head>  
  179. <body>  
  180.     <canvas id="myCanvas" width="600" height="300" style="border: 1px solid black;"></canvas>  
  181. </body>  
  182. </html>   
Output
 
graph2.jpg