Introduction
In this article I will walk through how to use a graphing equation in a 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.
<script type="text/javascript">
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext("2d");
</script>
Step 3
In the following code we will create a "Graph" function in which we define various methods, variables, constants and properties.
function Graph(config)
{
// user defined properties
this.canvas = document.getElementById(config.canvasId);
this.minX = config.minX;
this.minY = config.minY;
this.maxX = config.maxX;
this.maxY = config.maxY;
this.unitsPerTick = config.unitsPerTick;
// constants
this.axisColor = "#aaa";
this.font = "8pt Calibri";
this.tickSize = 20;
// relationships
this.context = this.canvas.getContext("2d");
this.rangeX = this.maxX - this.minX;
this.rangeY = this.maxY - this.minY;
this.unitX = this.canvas.width / this.rangeX;
this.unitY = this.canvas.height / this.rangeY;
this.centerY = Math.round(Math.abs(this.minY / this.rangeY) * this.canvas.height);
this.centerX = Math.round(Math.abs(this.minX / this.rangeX) * this.canvas.width);
this.iteration = (this.maxX - this.minX) / 1000;
this.scaleX = this.canvas.width / this.rangeX;
this.scaleY = this.canvas.height / this.rangeY;
// draw x and y axis
this.drawXAxis();
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.
Graph.prototype.drawXAxis = function ()
{
var context = this.context;
context.save();
context.beginPath();
context.moveTo(0, this.centerY);
context.lineTo(this.canvas.width, this.centerY);
context.strokeStyle = this.axisColor;
context.lineWidth = 2;
context.stroke();
Figure 1
The following figure represents the x-axis:
// draw tick marks
var xPosIncrement = this.unitsPerTick * this.unitX;
var xPos, unit;
context.font = this.font;
context.textAlign = "center";
context.textBaseline = "top";
// draw left tick marks
xPos = this.centerX - xPosIncrement;
unit = -1 * this.unitsPerTick;
while (xPos > 0)
{
context.moveTo(xPos, this.centerY - this.tickSize / 2);
context.lineTo(xPos, this.centerY + this.tickSize / 2);
context.stroke();
context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
unit -= this.unitsPerTick;
xPos = Math.round(xPos - xPosIncrement);
}
Figure 2
The following figure represents the left side tick mark of the x-axis:
// draw right tick marks
xPos = this.centerX + xPosIncrement;
unit = this.unitsPerTick;
while (xPos < this.canvas.width)
{
context.moveTo(xPos, this.centerY - this.tickSize / 2);
context.lineTo(xPos, this.centerY + this.tickSize / 2);
context.stroke();
context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
unit += this.unitsPerTick;
xPos = Math.round(xPos + xPosIncrement);
}
context.restore();
};
Figure 3
The following figure represents the right side tick mark of the x-axis:
Step 5
Similarily, we will draw the y-axis. On 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.
Graph.prototype.drawYAxis = function ()
{
var context = this.context;
context.save();
context.beginPath();
context.moveTo(this.centerX, 0);
context.lineTo(this.centerX, this.canvas.height);
context.strokeStyle = this.axisColor;
context.lineWidth = 2;
context.stroke();
Figure 4
The following figure represents the y-axis:
// draw tick marks
var yPosIncrement = this.unitsPerTick * this.unitY;
var yPos, unit;
context.font = this.font;
context.textAlign = "right";
context.textBaseline = "middle";
// draw top tick marks
yPos = this.centerY - yPosIncrement;
unit = this.unitsPerTick;
while (yPos > 0)
{
context.moveTo(this.centerX - this.tickSize / 2, yPos);
context.lineTo(this.centerX + this.tickSize / 2, yPos);
context.stroke();
context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
unit += this.unitsPerTick;
yPos = Math.round(yPos - yPosIncrement);
}
Figure 5
The following figure represents the upper side tick mark on the y-axis:
// draw bottom tick marks
yPos = this.centerY + yPosIncrement;
unit = -1 * this.unitsPerTick;
while (yPos < this.canvas.height)
{
context.moveTo(this.centerX - this.tickSize / 2, yPos);
context.lineTo(this.centerX + this.tickSize / 2, yPos);
context.stroke();
context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
unit -= this.unitsPerTick;
yPos = Math.round(yPos + yPosIncrement);
}
context.restore();
};
Figure 6
The following figure represent the lower side of the y-axis:
Figure 7
The following figure represents the combination of x-axis and y-axis. Both form a graph.
Step 6
In the following step we will draw the equation on the graph. We will apply the loop for drawing the equation.
Graph.prototype.drawEquation = function (equation, color, thickness)
{
var context = this.context;
context.save();
context.save();
this.transformContext();
context.beginPath();
context.moveTo(this.minX, equation(this.minX));
for (var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration)
{
context.lineTo(x, equation(x));
}
context.restore();
context.lineJoin = "round";
context.lineWidth = thickness;
context.strokeStyle = color;
context.stroke();
context.restore();
};
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.
Graph.prototype.transformContext = function ()
{
var context = this.context;
// move context to center of canvas
this.context.translate(this.centerX, this.centerY);
context.scale(this.scaleX, -this.scaleY);
};
Step 8
In the following code the window onload we will draw lines on the graph.
window.onload = function ()
{
var myGraph = new Graph({
canvasId: "myCanvas",
minX: -10,
minY: -10,
maxX: 10,
maxY: 10,
unitsPerTick: 1
});
myGraph.drawEquation(function (x) {
return 5 * Math.sin(x);
}, "green", 3);
myGraph.drawEquation(function (x) {
return x * x;
}, "blue", 3);
myGraph.drawEquation(function (x) {
return 1 * x;
}, "red", 3);
};
Example
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Graphing Equation in HTML5</title>
<script>
function Graph(con) {
// user defined properties
this.canvas = document.getElementById(con.canvasId);
this.minX = con.minX;
this.minY = con.minY;
this.maxX = con.maxX;
this.maxY = con.maxY;
this.unitsPerTick = con.unitsPerTick;
// constants
this.axisColor = "#aaa";
this.font = "8pt Calibri";
this.tickSize = 20;
// relationships
this.context = this.canvas.getContext("2d");
this.rangeX = this.maxX - this.minX;
this.rangeY = this.maxY - this.minY;
this.unitX = this.canvas.width / this.rangeX;
this.unitY = this.canvas.height / this.rangeY;
this.centerY = Math.round(Math.abs(this.minY / this.rangeY) * this.canvas.height);
this.centerX = Math.round(Math.abs(this.minX / this.rangeX) * this.canvas.width);
this.iteration = (this.maxX - this.minX) / 1000;
this.scaleX = this.canvas.width / this.rangeX;
this.scaleY = this.canvas.height / this.rangeY;
// draw x and y axis
this.drawXAxis();
this.drawYAxis();
}
Graph.prototype.drawXAxis = function () {
var context = this.context;
context.save();
context.beginPath();
context.moveTo(0, this.centerY);
context.lineTo(this.canvas.width, this.centerY);
context.strokeStyle = this.axisColor;
context.lineWidth = 2;
context.stroke();
// draw tick marks
var xPosIncrement = this.unitsPerTick * this.unitX;
var xPos, unit;
context.font = this.font;
context.textAlign = "center";
context.textBaseline = "top";
// draw left tick marks
xPos = this.centerX - xPosIncrement;
unit = -1 * this.unitsPerTick;
while (xPos > 0) {
context.moveTo(xPos, this.centerY - this.tickSize / 2);
context.lineTo(xPos, this.centerY + this.tickSize / 2);
context.stroke();
context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
unit -= this.unitsPerTick;
xPos = Math.round(xPos - xPosIncrement);
}
// draw right tick marks
xPos = this.centerX + xPosIncrement;
unit = this.unitsPerTick;
while (xPos < this.canvas.width) {
context.moveTo(xPos, this.centerY - this.tickSize / 2);
context.lineTo(xPos, this.centerY + this.tickSize / 2);
context.stroke();
context.fillText(unit, xPos, this.centerY + this.tickSize / 2 + 3);
unit += this.unitsPerTick;
xPos = Math.round(xPos + xPosIncrement);
}
context.restore();
};
Graph.prototype.drawYAxis = function () {
var context = this.context;
context.save();
context.beginPath();
context.moveTo(this.centerX, 0);
context.lineTo(this.centerX, this.canvas.height);
context.strokeStyle = this.axisColor;
context.lineWidth = 2;
context.stroke();
// draw tick marks
var yPosIncrement = this.unitsPerTick * this.unitY;
var yPos, unit;
context.font = this.font;
context.textAlign = "right";
context.textBaseline = "middle";
// draw top tick marks
yPos = this.centerY - yPosIncrement;
unit = this.unitsPerTick;
while (yPos > 0) {
context.moveTo(this.centerX - this.tickSize / 2, yPos);
context.lineTo(this.centerX + this.tickSize / 2, yPos);
context.stroke();
context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
unit += this.unitsPerTick;
yPos = Math.round(yPos - yPosIncrement);
}
// draw bottom tick marks
yPos = this.centerY + yPosIncrement;
unit = -1 * this.unitsPerTick;
while (yPos < this.canvas.height) {
context.moveTo(this.centerX - this.tickSize / 2, yPos);
context.lineTo(this.centerX + this.tickSize / 2, yPos);
context.stroke();
context.fillText(unit, this.centerX - this.tickSize / 2 - 3, yPos);
unit -= this.unitsPerTick;
yPos = Math.round(yPos + yPosIncrement);
}
context.restore();
};
Graph.prototype.drawEquation = function (equation, color, thickness) {
var context = this.context;
context.save();
context.save();
this.transformContext();
context.beginPath();
context.moveTo(this.minX, equation(this.minX));
for (var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
context.lineTo(x, equation(x));
}
context.restore();
context.lineJoin = "round";
context.lineWidth = thickness;
context.strokeStyle = color;
context.stroke();
context.restore();
};
Graph.prototype.transformContext = function () {
var context = this.context;
// move context to center of canvas
this.context.translate(this.centerX, this.centerY);
context.scale(this.scaleX, -this.scaleY);
};
window.onload = function () {
var myGraph = new Graph({
canvasId: "myCanvas",
minX: -10,
minY: -10,
maxX: 10,
maxY: 10,
unitsPerTick: 1
});
myGraph.drawEquation(function (x) {
return 5 * Math.sin(x);
}, "green", 3);
myGraph.drawEquation(function (x) {
return x * x;
}, "blue", 3);
myGraph.drawEquation(function (x) {
return 1 * x;
}, "red", 3);
};
</script>
</head>
<body>
<canvas id="myCanvas" width="600" height="300" style="border: 1px solid black;"></canvas>
</body>
</html>
Output