Source Allies Logo

Sharing Our Passion for Technology

& Continuous Learning

<   Back to Blog

A Basic Canvas

Over the weekend I decided to play around with the new html5 element canvas. The canvas allows you to paint images in the browser that previously was done with flash. Canvas is fairly straight forward and only requires a basic knowledge of html and javascript.

I decided a good start would be to try and make a thunderstorm in order to show the animation bit of canvas. In this article I will describe what I did and the issues that I had along the way. Finally at the end I will provide a working example with all of the code.

As you already know we must first include the new doc tag into the start of the code:

<!doctype html>

Next we will just include the canvas tag with an id.  We will give it an id since it will be more of a direct call when we start to use it later on.

<canvas id=’rain’></canvas>

We then get the element by id and start the 2d canvas.

var canvas = document.getElementById(‘rain’);
var cx         = canvas.getContext(‘2d’);

Now that we have the canvas we set the height of the canvas to the same size of the browser window in order to have it cover the full gray background.

canvas.width  = document.documentElement.clientWidth;
canvas.height  = document.documentElement.clientHeight;
cx.fillStyle  = "gray";
cx.fillRect(0,0,canvas.width,canvas.height);

If the run the above we would have a full gray window but we could easily do that with css and so far we are not doing anything special.  This next section we will be begin to make the rain.

There will be around 100 rain lines painted on the page, on start.  The rain lines also need to appear to be animated.  One issue that I ran into with making the rain lines animated is that once a line is painted there is no direct way to remove that line from the canvas without clearing it.  So we will keep track of the positions of each of the rain lines so that when we repaint the rain after the lighting there is no shift in the random positions of the rain lines and it is a smooth transaction.  Simple we will keep the past rain line positions in an array.

var rainPoints = [];

Making a single rain line will get a random position (x,y,z and time) for a line, paint a single line using rain colors, save it to the array of painted lines, and have a timer to remove that line and repaint a new one.  This way it will look like the canvas actually has rain on it.

The first part is getting the random time, and position of the rain to be drawn.  We will call this function getRainLine and it will take no arguments and return an object with random values.  I played around with the random times and found this combo looks real.

function getRainLine() {
var fallPoint = {
x: Math.floor((Math.random()*canvas.width)+1),
t: Math.floor((Math.random()*6000)+1000),
w: Math.floor((Math.random()*40)+1)
};
return fallPoint;
}

Now that we have a random position and time for a line we will go ahead and draw a line based on our getRainLine object.  This function will be called paintRainLine and it will take in the values that we generated from the function above.

function paintRainLine(x,w,color) {
cx.beginPath();
cx.moveTo(x,0);
cx.lineTo(x+w,canvas.width);
cx.strokeStyle = color;
cx.closePath();
cx.stroke();
}

In canvas we start by beginPath and then start at the x position that we randomly generated and then since rain falls from the sky the y is always 0.  LineTo will move that line to the bottom of the canvas.  The stokeStyle is different from stokeFill since it allows us to paint with gradient color.  We then close the path and call stroke to draw it on the screen.

Now that we know how to generate a single random rain line and paint it to the screen we go ahead and put that into another function in order to paint the lines, generate the colors, and set up a timer to remove the line and paint another.  We want to remove the line and paint another one since it does not just rain 100 random lines and then stop this is a thunderstorm and it rains a lot, and does not stop.  This next function we will call makeRain() and it will call all of the other rain functions that we just talked about.

function makeRain() {

var rain = getRainLine();
var gColor = cx.createLinearGradient(0,0,rain.x,canvas.width);
gColor.addColorStop(0,"rgba(171,205,239,0)");
gColor.addColorStop(1,"rgba(171,205,239,0.3)");

paintRainLine(rain.x,rain.w,gColor);
rainPoints.push(rain);

//Clears it and makes it rain again//
setTimeout(function() {
paintRainLine(rain.x,rain.w,"gray");
rainRemove(rain);
makeRain();
},rain.t);
}

Now that we have a way to constantly paint rain on a webpage we go ahead and add this on the page start up in order to start the rain once the user visits the webpage.  Starting with 99 lines looks good so we just put that into a for loop and start 99 lines at start that are all self starting.

/* how many rain lines at one time */
for (var x = 1; x &lt; 100; x++) {
makeRain();
}

Just running the code above will give you a nice rain storm but we wanted a thunderstorm so we need to go ahead and add in some random lighting.  Drawing lighting I was not sure how to go about this since I just wanted to use straight lines and not start to use curved lines or anything complex. So I devised a solution in order to do this.

/* Returns a line that is all strange looking */
function getLightArray() {

var lastX = Math.floor((Math.random()*canvas.width-60) + 3);
var lastY = 0;
var newLight = [lastX,lastY];

var direction = Math.floor(Math.random()*2);

for (var i = 0; i &lt; 15; i++) {
var rndX = Math.floor(Math.random()*30);
var rndY = Math.floor(Math.random()*60);
if (direction) {
newLight.push(rndX+lastX,rndY+lastY);
lastX = rndX+lastX;
lastY = rndY+lastY;
} else {
newLight.push(lastX - rndX,rndY+lastY);
lastX = lastX-rndX;
lastY = lastY+rndY;
}
}

return newLight;
}

The function getLightArray will return an array with the positions that will be used to draw a random lighting.  Not all lighting is the same so we need a random lighting just like we need a random rain pattern.  The getLightArray will start to draw a straight line, then depending it will split off and kind of look like several zigzags.

Several zigzags does not look like lighting so we will have something like looks like static split out of the tips of the zigzags in a different function. That function will be called getStaticLine.

function getStaticLine(inStart,inEnd,direction) {
var newDirection = [inStart,inEnd];

for (var i = 0;i &lt; 3+Math.floor(Math.random()*5); i++) {

var rndX = Math.floor(Math.random()*20);
var rndY = Math.floor(Math.random()*20);

if (direction) {
inStart = inStart - rndX;
inEnd   = inEnd + rndY;
} else {
inStart = inStart + rndX;
inEnd   = inEnd + rndY;
}

newDirection.push(inStart,inEnd);
}

return newDirection;
}

Now that we have a function for making a zigzag line and a function for creating static to come off the line at points we need to put it together with a function we will call findPoints and it will take in a new getLightArray() and determine where to draw the statics at.

function findPoints(inPoints) {
var maxValue  = 4;
var lastValue = inPoints[0];
var staticPoints = [];
for (var i = 0;i &lt; inPoints.length; i = i+2) {
if ( (maxValue + lastValue) &lt;= inPoints[i] || (maxValue + lastValue) &gt;= inPoints[i]) {
staticPoints.push(inPoints[i],inPoints[i+1]);
lastValue = inPoints[i];
}
}

return staticPoints;
}

We now have all of the elements in place and ready to start the lighting and set it up on a timer like we do with the rain for a constant lighting strike. This function will be called lightShow(). The lightShow function will begin by starting a line, then grabbing a getLightArray, drawing the light array using drawLight, and getting the sharp edges using findPoints. We will then traverse points that we found using findPoints and make the static lines on each of the points using getStaticLine. We now have a raining image with a single lighting bolt that strikes from the sky and just stays in place. Clearly lighting only lasts for a blink of the eye so we must remove that lighting. I tried to just draw over the lighting with the same color as the background but that did not work and there is no real way to just remove drawn lines after they are painted so we will redraw the full canvas. Good thing we have been saving all of the running rain positions.  So we simply just clear the canvas, paint the background, and then redraw the rain.  Lastly we set up a timer to repaint another random lighting strike in a few seconds. Below is the code.

function lightShow() {
cx.beginPath();
cx.lineJoin="round";

var lightArray = getLightArray();
drawLight(lightArray,"white");
var savePoints = findPoints(lightArray);

var flag = 1;
var totalStaticPoints = [];
for(var i = 0; i &lt; savePoints.length; i = i+2) {
for (var p = 0; p &lt; Math.floor(Math.random() * 3)+3; p++) {
var staticTest = getStaticLine(savePoints[i],savePoints[i+1],flag);
drawLight(staticTest,"white");
totalStaticPoints.push(staticTest);
}
flag = Math.floor(Math.random()*2);
}

cx.stroke();

setTimeout(function() {
/*
drawLight(lightArray,"gray");
for (var i = 0; i &lt; totalStaticPoints.length; i++) {
drawLight(totalStaticPoints[i],"gray");
}
cx.stroke();
*/
cx.fillStyle = "gray";
cx.fillRect(0,0,canvas.width,canvas.height);

/*
for (var i = 0; i &lt; 100; i++) {
var rain = getRainLine();
paintRainLine(rain.x,rain.w,gColor);
}
*/

for (var i = 0; i &lt; rainPoints.length; i++) {
var gColor = cx.createLinearGradient(0,0,rainPoints[i].x,canvas.width);
gColor.addColorStop(0,"rgba(171,205,239,0)");
gColor.addColorStop(1,"rgba(171,205,239,0.3)");
paintRainLine(rainPoints[i].x,rainPoints[i].w,gColor);

setTimeout(function() {
paintRainLine(rainPoints[i].x,rainPoints[i].w,"gray");
rainRemove(rainPoints[i]);
//makeRain();
},rainPoints[i].t);

}

setTimeout(function() { lightShow(); },10000);
},1000);

}

I hope to have described everything accurate and hope that you have learned something. This was the first time that I worked with canvas and I have learned a lot since then but it is still a working example.