Download the code for Change Colors.
In this exercise, we'll introduce two new concepts—Mouse events and the Math object's random() method. We'll use both to change the color of a circle when its clicked on.
Mouse events are one way to make your projects more interactive. When you click down with your mouse, that's a mousedown event. When you release your mouse, that's a mouseup event. When you move your mouse, that's a mousemove event. Your computer's operating system forward all those events to your browser and we can access those events with our JavaScript code.
For this exercise, we'll focus on two mouse events:
mousedown (you clicked your mouse down)
mousemove (you moved your mouse).
We'll change the colors of our circle in this exercise by using JavaScript's Math object and its method random() which returns a random decimal fraction between 0 and 1. With random() and a little math, we'll generate random colors for our circle. Let's get to the code.
Like before, make a simple HTML page—change the title to Change Colors add a canvas tag along with a script tag with its src set to "js/change-color.js". Here's our HTML code:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Change Colors</title>
<link href="css/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="interaction">
<canvas id="myCanvas" width="1200" height="800"></canvas>
</div>
<script src="js/responsive-canvas.js"></script>
<script src="js/change-colors.js"></script>
</body>
</html>
I've included main.css and responsive-canvas.js in our page to make our canvas more responsive. Take a look at Format the Canvas to review how to dynamically scale the canvas.
Next, create a Javascript file in Dreamweaver and save it as change-colors.js.
Here's our initialization of our variables:
(function() {
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
canvasW = canvas.width,
canvasH = canvas.height,
circX = canvasW/2,
circY = canvasH/2,
radius = 200,
startRad = 0,
endRad = Math.PI *2,
RED_MAX = 255,
GREEN_MAX = 255,
BLUE_MAX = 255,
//Make your canvas responsive to the browser's window size
myRC = new ResponsiveCanvas({canvasId:'myCanvas', divId:'interaction'});
//resize the canvas
myRC.resizeCanvas();
})();
We're setting up our canvas and context, storing the canvas' width and height, and setting up the attributes of the circle shape. We're also setting the maximum values for the red, green, and blue color channels; each channel can have a value that ranges from zero to 255. These color variables are like constant variables in other programming languages—they stay at the same value throughout the script. The convention is to write constant variables in all caps.
Note that I'm setting up the responsive canvas code at the end of our variables. As I wrote above, you can find more information about this in the Format the Canvas exercise. I'm also wrapping our code inside a self-executing anonymous function. Learn more about that from the Variable Scope and Closure web page.
Now let's draw a circle using the variables we just created. We'll set the fill style to black—'rgb(0,0,0)'—start our path, use the arc() method with our variable arguments, and then fill the shape. Add this code below your variables:
context.fillStyle = 'rgb(0,0,0,)';
context.beginPath();
context.arc(circX, circY, radius, startRad, endRad);
context.fill();
Save your JavaScript file, open your HTML page in a browser and verify that you have a black circle in the center of your Canvas.
Always test out your code as you go—write a little code, check it in a browser, look for problems in your browser's Console panel. Fix any syntax, logic, or organizational problems in the code and when everything's working well, then head back to your code and add a few more lines before you test it out again.
This back and forth, mixing coding and testing, ensures you don't repeat bad code throughout your script and helps you target the part of your code that may be having problems. If you've only added a few lines of code and something's broken, then you look first at those last few lines for errors and you're much less likely to make incorrect assumptions about where the code problem lies. Let the browser act as a by-your-side editor, telling you when your last few lines need a little re-working.
Now let's set up a mouse event so that when someone clicks on our page, we can respond by changing the circle's color.
We do that by setting up an event listener—we're listening for a specific event to happen and when it does we call a function (remember a function is a block of reuseable code similar to an object's method). This is the code that sets up our event listener:
canvas.addEventListener('mousedown', changeColor);
We've attached the addEventListener method to our canvas and passed two arguments. The first argument, 'mousedown', is predefined—it's the name of the event that we're listening for. A 'mousedown' event happens when you click down on a web page. Some other predefined mouse events are:
'mouseup' (you released the mouse)
'mousemove' (you're moving the mouse)
'mouseover' (your mouse hovers over an element on your page)
Notice that each predefined event type is surrounded by quotes.
After the 'mousedown' argument, our second argument is changeColor—that's the name of the function we'll write. When the mousedown event happens, our changeColor function is called. Here's the changeColor function:
function changeColor() {
context.clearRect(0, 0, canvasW, canvasH);
context.fillStyle = 'rgb(' + Math.floor(Math.random() * RED_MAX) +',' + Math.floor(Math.random() * GREEN_MAX) + ',' + Math.floor(Math.random() * BLUE_MAX) + ')';
context.beginPath();
context.arc(circX, circY, radius, startRad, endRad);
context.fill();
}
Notice the syntax for our function. We start with the key word function and then give the function a name. Make your function name descriptive—if it's a multi-word name, use camel case formatting with no spaces between the words.
The function name is followed by parentheses (just like a method). We're not passing any arguments to this function, but if we were, the parameters that capture those arguments would be placed inside the parentheses. After our parentheses, we have a left-facing curly bracket—this indicates the start of our function's code. Notice the right-facing curly bracket several lines down. That's the end of our function. Any code inside those two curly brackets runs when our function is called.
Our first line of code invokes the clearRect() method—this method erases whatever you have on your canvas (or a portion of your canvas). We need to clear the canvas to remove the current circle and replace it with a new circle that has a new color.
We pass four arguments to clearRect() that define what part of the canvas to clear: the first two arguments are clearing rectangle's top left corner X and Y coordinates; our last two arguments are the rectangle's right corner X and Y coordinates.
Our arguments place the clearing rectangle's top left corner at (0,0)—the top left corner of the canvas—and place the clearing rectangle's bottom right corner at the canvas' width and height. So we're clearing out the entire canvas.
Our next line of code sets our context's fillstyle to random RGB values. We use Math's random() method—a method that returns a random decimal fraction between 0 and 1—and multiply that random number by the red, green, and blue's maximum values.
Let's take a look at a section of that line of code:
Math.random() * RED_MAX
We initialized RED_MAX to 255 at the top of our script. Let's say that Math.random() returns the fraction 0.5, so our calculation equal 127.5—that's close to a dark red in the middle of our red channel's range. Next we need to round our 127.5 number so it's a whole integer—rgb() format expects integer values. We do that with another Math method, Math.floor().
The floor() method takes the number you pass it and returns a whole number that's less than or equal to the number you passed it. So Math.floor(127.5) returns 127.
Look again at the code: we pass our first calculation (Math.random() * RED_MAX) as the argument for Math.floor(). So we first multiply a random number by 255 and then pass that calculation to the Math.floor() method which rounds that calculation to a whole number. It looks like this:
Math.floor(Math.random() * RED_MAX);
We do this for each color channel, getting a random number for red, green, and blue.
In Canvas, we can set color values with an RGB color string: here's an example of this kind of string:
'rgb(172, 0, 172)'
This color string is the equivalent of purple—we have a middle value red, no green at all, and middle value blue. The red and blue together create purple.
We need to dynamically build RGB color strings; that is, we need to insert random numbers for color values with each mouse click. We can combine strings and numbers (even mathematical calculations) using a plus sign. Remember our very first JavaScript exercise, Hello World? We used the console.log method to print out a string and the value of our canvas's width and height properties to our browser's Console panel.
We concatenated or combined string and canvas properties with a plus sign like this:
console.log('Hello Again ' + canvas.width + ' ' + canvas.height);
We're doing a similar thing—combining strings and numbers— to build our RGB color values.
context.fillStyle = 'rgb(' + Math.floor(Math.random() * RED_MAX) +',' + Math.floor(Math.random() * GREEN_MAX) + ',' + Math.floor(Math.random() * BLUE_MAX) + ')';
After we dynamically create our colors, we start our path, invoke the arc method, and fill the circle. And, finally, we close off the function with a right-facing curly bracket. Here's all the code so far:
//Changing Colors
//Click on the circle to change its color
(function() {
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
canvasW = canvas.width,
canvasH = canvas.height,
circX = canvasW/2,
circY = canvasH/2,
radius = 200,
startRad = 0,
endRad = Math.PI *2,
RED_MAX = 255,
GREEN_MAX = 255,
BLUE_MAX = 255,
//Make your canvas responsive to the browser's window size
myRC = new ResponsiveCanvas({canvasId:'myCanvas', divId:'interaction'});
//resize the canvas
myRC.resizeCanvas();
//draw your circle
context.fillStyle = 'rgb(0,0,0,)';
context.beginPath();
context.arc(circX, circY, radius, startRad, endRad);
context.fill();
//listen for a mousedown event and call changeColor when a mousedown event occurs
canvas.addEventListener('mousedown', changeColor);
//give the circle a new color
function changeColor() {
context.clearRect(0, 0, canvasW, canvasH);
context.fillStyle = 'rgb(' + Math.floor(Math.random() * RED_MAX) +',' + Math.floor(Math.random() * GREEN_MAX) + ',' + Math.floor(Math.random() * BLUE_MAX) + ')';
context.beginPath();
context.arc(circX, circY, radius, startRad, endRad);
context.fill();
}
})();
Add the event and function to your code and test it in your browser. With every mouse click, you'll see a circle with a new random color.
Next we'll make the circle clickable, so that the color changes only when you mouse clicks on the circle