Back at the beginning of 1997, I began to learn HTML. Boy, was it easy. Back then, it was just a matter of placing a tag on a page. After that, I learned that I should close that tag with another tag. Building web pages was easy. Fiddle around in Photoshop and make a banner graphic. Instant web page!
It’s a good thing it’s not the beginning of 1997 or else I would be trouble…
Just when you thought HTML was just a tag with some attributes and followed by a closing tag, you thought wrong. In my last HTML5 post about Drag and Drop, I point out that HTML no longer stands on it’s own and that JavaScript will be supplementing HTML5.
What is Canvas?
The Canvas element allows for a developer to create dynamically generated pictures using JavaScript and one HTML tag. Allow me to introduce the Canvas tag:
<canvas id="canvasTag" width="200" height="200">This browser does not support Canvas!</canvas>
This is the only HTML you need to display your Canvas artwork on a web page. The remaining 99% of the work is done with JavaScript. Of course, you don’t need to use the width or height attributes as I will show you below. But, at least you know you can use them.
I have built a small application using Canvas. It sits on my HTML5/CSS 3 test page. Basically, the app allows you to enter 3 sets of numbers into fields in a form. These fields are marked “Month 1”, “Month 2” and “Month 3”. While I don’t have an actual point to the application, I want you to use your imagination. Maybe your company needs sales figures for a series of months. The form will save the relevant information to the database via AJAX and draw up a bar chart for users to see. No need to turn to Photoshop to create a static chart.
NOTE: This is not intended to be a tutorial. I am merely showing you what Canvas is and does by using a potential real-world example. If you want to learn how to draw circles, rectangles and whatever else, there is a ton of information on the interwebs. Just Google html5 canvas to get started!
The Bar Graph Application
I need to show information in a 3-month span in bar graph format. I add the Canvas tag to my page.
<canvas id="monthBarChart" style="float:left; text-align:left; margin:auto;"></canvas>
I don’t need to use the width and height attributes because I can set them in the JavaScript code using the setAttribute() method. When I do this, it basically wipes the slate clean so I can draw a new chart. The Canvas element is declared in a variable so it can be used later. Next, I need to make sure the browser likes Canvas by testing the getContext object.
var monthBarChart = document.getElementById("monthBarChart");
if(monthBarChart && monthBarChart.getContext) {
If all is well, the code moves to the next part which is to prepare the Canvas for drawing. This is done by setting the getContext() object in “2d” (3d is not supported yet) and declaring the object in a variable.
chartCtx = monthBarChart.getContext("2d");
monthBarChart.setAttribute("width", 250);
monthBarChart.setAttribute("height", 250);
It’s better to set the width and height within the JavaScript code so, you can erase the Canvas. If you don’t do this, the graph will remain and the new one will be drawn over it.
Drawing lines
Drawing lines requires a path. Imagine looking at the piece of paper and figuring out the spot where you are going to place your pencil and the spot to where you are going to draw. Next, you draw your line. Drawing another line? This requires moving the pencil from the previous spot and moving it to the new spot. Check out the code for drawing the main, black horizontal and vertical lines:
//LAYER 1: Vertical outside line
chartCtx.beginPath();//New path
chartCtx.moveTo(30,2);//pixels
chartCtx.lineTo(30,230);//pixels
chartCtx.strokeStyle = "#000000";
chartCtx.stroke();
//LAYER 1: Horizontal outside line
chartCtx.moveTo(30,230.5);
chartCtx.lineTo(245,230.5);
chartCtx.closePath();//Ending the path
chartCtx.strokeStyle = "#000000";
chartCtx.stroke();
Drawing a line in Canvas requires the beginPath() function. If consecutive lines are to be drawn, beginPath() only needs to be called once. Using moveTo(x,y) is just the same as picking up a pencil and moving it to the spot where drawing will begin. Using lineTo(start,finish) is the same as drawing a line from one spot to another. Once moveTo() and lineTo() are set, you set the color of your line and then draw it by using stroke(). Canvas draws a horizontal line from right to left and a vertical line from top to bottom.
Text
Drawing text on a canvas requires CSS-like settings before text is actually added.
chartCtx.font = "9pt Arial";
chartCtx.textBaseline = "top";
chartCtx.fillStyle = "#000";
Text can also be bolded by adding bold before the size of the text. In my tests, I noticed that bold looks aliased in Safari. I don’t really understand what s going on there. Maybe it was the size of the text?
In order to add a list of number to the graph, the starting y pixel and starting number must be set. Then, a for loop must be run (loop 5 times).
//LAYER 3: Chart number amounts
var yStart = 1;//The y pixel the number list starts at
var amountStart = 500;//The amount that is displayed first
for(b = 0; b < 5; b++) {
chartCtx.fillText(amountStart, 2, yStart);//fillText("text to be written", x, y)
yStart = (Number(yStart) + 46);
amountStart = (Number(amountStart) - 100);
}
The reason the y pixel is set (to 1) and declared is because each time the loop is run, 46 pixels must be added to the fillText() y position. 46 pixels separate each grey horizontal line in the chart. The amountStart number decreases by 100 every time the loop is run. The numbers are drawn in this order: 500, 400, 300, 200 and 100.
Bars
The amount of bar that is made is dependent on what the user enters into the field. Of course, in order to do this, a formula must be worked out.The intial formula I worked out for my application: 100/46 = 2.173.
This formula took some trial and error and to be honest, I pulled this one out of my ass. Since each number on the chart is counted by one-hundred, 100 is divided by the number of pixels between each grey line on the chart, 46 in this case. This equals 2.173913043478261. Of course, it is not necessary to use the entire number so I just used 2.173 and I did not round off to the nearest one-thousandth.
Next, I used this formula to calculate the height of a rectangle: (value of a month field/2.173) – 2.
I subtracted 2 from the remainder of the month field value/2.173 because the graph bar height was off by just a little bit every time I entered 100, 200, 300 etc. into a month field. This is the code to make the bars:
//LAYER 5: Bars
var monthArr = new Array(document.getElementById("month1Field"), document.getElementById("month2Field"), document.getElementById("month3Field"));
var error_var = 0;
for(c = 0; c < monthArr.length; c++) {
if(monthArr[c].value < 0 || monthArr[c].value > 500) {
error_var++;
}
}
if(error_var > 0) {
alert("You can only choose a number between 0 and 500!");
}
else {
var monthColorArr = new Array("rgb(0, 100, 100)", "rgb(100, 100, 100)", "rgb(0, 153, 0)");
var rectX = 40;//Where the first bar will be drawn
//100/46 = 2.173
for(d = 0; d < monthArr.length; d++) {
if(monthArr[d].value > 0) {
var rectangleHeight = ((Number(monthArr[d].value)/2.173) - 2);
}
else {
var rectangleHeight = 0;
}
var rectY = (230 - Number(rectangleHeight));
chartCtx.fillStyle = monthColorArr[d];
chartCtx.fillRect(Number(rectX), Number(rectY), 50, Number(rectangleHeight));
rectX = (Number(rectX) + 70);
}
}
chartCtx.restore();
When a rectangle is made, a color is defined using fillStyle and CSS: canvasElem.fillStyle=”rgb(0,0,0)”.
Next, canvasElem.fillRect(x,y,width,height) is used to draw out the actual rectangle. It’s pretty simple. The for() loop runs until all three fields are found and graph bars are made out of them. A user can fill out one, two or all three fields – it does not matter.
In Conclusion
I have to admit that creating graphics on the fly is killer. The possibilities with Canvas are only limited by, well, it’s limitations. There is a feature that has not been added on, even though there has been considerable discussion about it and request for it. For example, in my WordPress dashboard, a graph is created that shows how many hits I get. The cool thing is that I can mouse over certain points and see when a new post was created. While you can add event attributes to the Canvas element itself, without a great deal of work, you cannot add events to shapes within the Canvas. I read somewhere of people suggesting a Usemap like feature but it was shot down for the moment.
While Canvas is not supported in Internet Explorer 8 and below, there is a project called “explorercanvas”.
While visiting my HTML5/CSS 3 test page, you may have noticed my CSS 3 Fun Box. I will be talking about that in my next blog posting