Download the code for this draw-and-rotate bitmaps exercise
Up to this point we created images using Canvas' fill-and-path methods like rectFill() and arc(). In this exercise, we'll draw bitmap images—images made of pixels, the kind of images you create when you snap a photo with a digital camera or draw an image in an application like Photoshop.
In this exercise, we'll introduce two new concepts—events and functions. We'll use events and functions to help us load a graphic and rotate it on the canvas.
Events are happening all the time in digital media. If you press a key on your keyboard, that's an event. If you click your mouse or touch your screen, that's an event. If a page loads in your browser, that's an event. If you resize your browser's window, that's an event too. Your operating system and browser track events and we use JavaScript to respond to those events.
For this exercise, we'll focus on the load event—an event that occurs when a file has finished downloading from the web.
Functions are self-contained blocks of code. They let you reuse a section of code and help clarify the structure of your scripts. The methods we've used so far in this class—methods like rect() and arc()—are basically functions attached to objects. Both methods contain blocks of reusable code that draw shapes based on the arguments we send to them. Functions work in a similar way—they perform a simple action and you can pass them arguments to modify their action.
Let's get started on our script that loads and draws a bitmap graphic. First, make a simple HTML page—change the title tag to Bitmaps, link our formatting main.css file, and add a canvas tag and a script tag with its src set to 'js/draw-bitmap.js'.
Here's our HTML code:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Bitmaps</title>
<link href="css/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="interaction">
<canvas id="myCanvas" width="1200" height="800">
You'll need a contemporary browser with javascript
turned on to be able to see this page
</canvas>
</div>
<script src="js/responsive-canvas.js"></script>
<script src="js/draw-bitmap.js"></script>
</body>
</html>
Like before, I've included the main.css and responsive-canvas.js files. Since the code inside responsive-canvas.js will be referenced inside our draw-bitmap.js file, add the responsive-canvas.js script tag before you include the draw-bitmap.js script tag. Also, don't forget to wrap the canvas in the div tag with an id of interaction. The responsive-canvas script expects that div tag with its id to be there. (See Format the Canvas for more information on creating a canvas that changes its size based on the browser's window size.)
Back to our pixel-based graphics. The procedure to load a bitmap graphic into the canvas goes like this: you create an image object, assign it a source, and when your image is loaded, you draw your image to the canvas.
Let's first initialize our variables at the top of our script:
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
canvasW = canvas.width,
canvasH = canvas.height,
//create an image object
imageObj = new Image(),
imgSrc = 'img/oval.png',
//Make your canvas responsive to the browser's window size
myRC = new ResponsiveCanvas({canvasId:'myCanvas', divId:'interaction'});
//resize your canvas
myRC.resizeCanvas();
Most of this should look familiar. We set up the canvas and context, store the width and height of the canvas for later, and set up the code to make our canvas responsive to changes in browser size.
We have two new variables—the first is the imageObj variable that's set to an instance of an Image object (new Image()). We also set up imgSrc—a string variable that stores the relative path of our bitmap image, a PNG image called oval.png, housed inside a folder called img.
So far so good. Next we need to capture an event that tells us our image is downloaded. The load event does just that. Here's the code:
imageObj.addEventListener('load', imageLoaded);
This code attaches an addEventListener method to our image object. addEventListener can be attached to an image object, the canvas, or any element you've added to your HTML page. When the method is attached, it listens for a specific event to happen. When the event occurs, addEventListener calls a function that responds to the event (recall that a function is a block of reuseable code similar to an object's method). In our example, when our image is loaded, we call a function named imageLoaded. Let's write the imageLoaded function next:
function imageLoaded() {
context.drawImage(imageObj, canvasW/2, canvasH/2);
}
We have just one line of code within our imageLoaded function. This line uses the method drawImage to draw our bitmap to the context. In this example, drawImage takes three arguments: the image object, and the screen position (X and Y coordinates) where we want to draw our image on the canvas.
Our last line of code looks like this:
imageObj.src = imgSrc;
Here we set the image object's src (source) property to our bitmap's file path. For most contemporary browsers, we could add this line of code earlier in the processes, but some older browsers expect the image's source to be set after the load event code. So just to be safe, we're adding this line of code at the end.
Here's the complete code to draw a bitmap image:
//Draw Bitmap
//Load and display a bitmap graphic
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
canvasW = canvas.width,
canvasH = canvas.height,
//create an image object
imageObj = new Image(),
imgSrc = 'img/oval.png',
//Make your canvas responsive to the browser's window size
myRC = new ResponsiveCanvas({canvasId:'myCanvas', divId:'interaction'});
//resize your canvas
myRC.resizeCanvas();
//attach an onLoad event to the image and call the imageLoaded function when the image is downloaded
imageObj.addEventListener('load', imageLoaded);
function imageLoaded() {
//draw the image for the first time
context.drawImage(imageObj, canvasW/2, canvasH/2);
}
//set your image object's source to a bitmap graphic
imageObj.src = imgSrc;
You'll need to create a bitmap—make something simple in Photoshop or another image editor. In Photoshop, use File>Export as to save your image in a web-friendly format; gifs, jpgs, or pngs all work for canvas. Update the imgSrc variable in your script with your own image's relative file path. (My image was called oval.png and saved in a subfolder called img, so I set my imgSrc variable to "img/oval.png".) Save out your JavaScript file and open your HTML file in a browser. You'll see a bitmap image drawn at the bottom-right quadrant of your canvas.
Up to this point, we haven't rotated any graphics on our canvas' context. Let do that next.
First we need to add three new variables at the top of our script: imgW, imgH, and radians. The first two store the height and width of our bitmap image—my image's dimensions are 420x240 pixels. The third variable, radians, helps us rotate the canvas.
Remember the beginning and ending radians we passed as arguments to context's arc() method to create a circle? We're using the same radian measurements here. Radians are just a way to measure angles—to cover a full circle with radians, start at zero and go up to two times PI (around 6.28).
We can map a radian's value to a clock's face: zero radians start at 3 o'clock, PI radians (around 3.14 or 180 degrees) line up at 9 o'clock, and two times PI (around 6.28 or 360 degrees) end up back at 3 o'clock.
Add these new variables to our variable list:
//Rotate Bitmap //Rotate a bitmap graphic 1.2 radians var canvas = document.getElementById('myCanvas'), context = canvas.getContext('2d'), canvasW = canvas.width, canvasH = canvas.height,
radians = 1.2, imgW = 420, imgH = 240,
//create an image object imageObj = new Image(), imgSrc = 'img/oval.png', //Make your canvas responsive to the browser's window size myRC = new ResponsiveCanvas({canvasId:'myCanvas', divId:'interaction'}); //resize the canvas myRC.resizeCanvas();
Whenever you rotate a graphic, you'll need to think about the pivot point of your rotation. The pivot point is the place on your graphic around which everything spins. If you place the pivot point at the center of your graphic, it'll spin at the middle. If you place the pivot point at the top left corner of your graphic, the graphic swings around that top-left point.
To create a pivot point, you adjust the x and y arguments of context's drawImage method after you translate (or move) the entire context. In this example, we want to rotate our graphic at the center of our canvas with a pivot point that's at the middle of the graphic. Before we do that, we'll save the current context; at the end of our rotation, we'll return the context back to its original state, restoring the placement of any other elements we have on our context.
We save the context with a save() method and restore the context with a restore() method. We move the canvas with a translate() method and rotate it with a rotate() method. All pretty straightforward.
Here's the code for our function called rotateImage(), add it to the bottom of your script before the closing, right-facing curly bracket.
function rotateImage() {
context.save();
context.translate(canvasW/2, canvasH/2);
context.rotate(radians);
context.drawImage(imageObj,-imgW/2, -imgH/2);
context.restore();
}
Also, replace the drawImage inside the imageLoaded function with a call to rotateImage():
function imageLoaded() {
rotateImage();
}
Let's discuss a little more the relationship of the pivot point to the context's translate and rotate methods. Here's one way to think about it: we're basically drawing our graphic's pivot point at context's upper left corner (0,0) and then we move or translate the context to the place we want the image to appear on our canvas, and finally we rotate the context. The actual order of code is a little different—translate and rotate precede our drawImage method—but imagining we draw the image first tends to be an easier way to visualize the process.
Think of the context as a sheet of paper that can move inside the frame of the canvas. We move that sheet to the position where we want to draw our graphic. Then we rotate the sheet and draw our graphic on the top left corner of the sheet.
Notice that we pass drawImage two negative numbers for its X and Y arguments: the first is negative half the image's width; the second is negative half the image's height. This offsets the placement of the image so that our pivot point is at the center of our image. The image's pivot point aligns with context's top left corner and our image hangs off the context by half its width and half its height.
We can create other pivot points for our image. If we want a pivot point at the graphic's top left corner, we'd pass drawImage() these arguments:
context.drawImage(imageObj, 0, 0);
If we want our graphic's pivot point at its bottom-right corner, we'd offset the graphic's whole width to the right and push its whole height up. The code would look like this:
context.drawImage(imageObj, -imgW, -imgH);
Try out these drawImage variations in your code to get a feel for how the pivot point works in concert with the translate and rotate methods.
Here's the complete code:
//Rotate Bitmap
//Rotate a bitmap graphic 1.2 radians
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
canvasW = canvas.width,
canvasH = canvas.height,
radians = 1.2,
imgW = 440,
imgH = 240,
//create an image object
imageObj = new Image(),
imgSrc = 'img/oval.png',
//Make your canvas responsive to the browser's window size
myRC = new ResponsiveCanvas({canvasId:'myCanvas', divId:'interaction'});
//resize the canvas
myRC.resizeCanvas();
//attach an load event listener to the image and
//call the imageLoaded function when the image is downloaded
imageObj.addEventListener('load', imageLoaded);
function imageLoaded() {
//draw the rotated image
rotateImage();
}
//set your image object to a bitmap graphic
imageObj.src = imgSrc;
function rotateImage() {
context.save();
//move to the center the of the canvas
context.translate(canvasW/2, canvasH/2);
//rotate the canvas by 1.2 radians
context.rotate(radians);
//draw from the graphic's center point
context.drawImage(imageObj,-imgW/2, -imgH/2);
//restore the context to its original state
context.restore();
}
Save your JavaScript file and open your HTML file in a browser. Your bitmap image is rotated 1.2 radians.
Download the code for this draw-and-rotate bitmaps exercise.