context.fillStyle will set the color for all future fills such as
context.fillRect(). It's important to note theat context.fillStyle
is not a method, it is an attribute. You set it like so:
context.lineTo(x, y) creates a path from the current point - default (0,0) -
to the point (x, y). In order to see the line you must call context.stroke().
context.save() saves all of the properties of the context. Then, when you call
context.restore() it will reset all changes to the context you've made.
This code will produce the image below (note how the second rectangle is black
and closer to the original origin despite calling translate and setting the
fillStyle earlier):
context.scale(percentX, percentY) scales the x and y coordinates of the context, making drawings
larger or smaller.
This code will produce the image below (note that all rectangles are defined to be the same size
in the code are drawn in different sizes due to the scale of the context):
context.translate(x, y) shifts the origin of the context (where the (0,0) point is)
right by x and down by y. This allows you to draw execute multiple commands in a specific
area of the context more easily.
This code will produce the image below (note how the fillRect commands are
relative to the translation point (100,100)):
Math.round(), Math.floor() and Math.ceil() will round a number to an integer.
Math.floor() will always round down while Math.ceil() will always round up.
Math.round() will round to the closest integer, either up or down.
Math.random() will produce a random decimal betweek 0 and 1. Calling it many
times will produce a different random number each time.
Often you will multiple Math.random() by another number to get a random
integer larger than 1. You may also call Math.floor() or Math.ceil() on the result
to get an rounded integer.
In the first stage of the Cityscape Challenge, we will draw the shapes of
buildings with different sizes. Because we will be drawing lots of buildings,
we will automate the process using functions and variables.
Lesson: Drawing In The Coordinate System
When drawing on canvas it is important to understand how the
coordinate system
works, specifically that
"y" values go down.
Unlike with a normal graph, where the positive "y" values go up, in the canvas
positive "y" values go down from the top.
So the point (50, 100) is 50 pixels from the right of the top left corner of the canvas and
100 pixels down from the top left corner.
In the example below, the black square is at (0, 0) so it's top left corner
is in the top left corner of the canvas. The blue square is at (50, 100)
so it's top left corner is 50 pixels to the right and 100 pixels down.
Try adjusting the coordinates of the two squares and see how they move around
the coordinate system.
Important:This is a lesson, not a challenge.
It is here just to help you learn. Play around with it to see your code changes
affect the result. Scroll down for the first challenge.
Editor
(write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage1_lesson1');
var context = canvas.getContext('2d');
context.fillRect(0, 0, 50, 50);
context.fillStyle = 'blue';
context.fillRect(50, 100, 50, 50);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 1
Draw a rectangle using the code
context.fillRect(x, y, width, height)
where you replace x with the top left x coordinate, y with the top left y coordinate,
width with the width of the rectangle and height with the height of the rectangle.
Draw the rectangle so that it's top left corner is 60px to the right of the top
left corner of the canvas and 90px down. The rectangle should be 45px wide (width) and
45px tall (height).
In the end your drawing should look like the example provided to the right.
var canvas = document.getElementById('granular_basic_cityscape_stage1_challenge1');
var context = canvas.getContext('2d');
// YOUR CODE HERE
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage1_challenge1');
var context = canvas.getContext('2d');
context.fillRect(60, 90, 45, 45);
Lesson: The Ground
When drawing a cityscape you need to to draw all of the buildings
sitting on the ground.
This complicates our drawing because we need to draw each building
as a rectangle that starts from the top left corner, but we need
to draw it relative to the ground, not the top.
This example shows the ground as a simple line, 240px from the top
of the canvas.
Important:This is a lesson, not a challenge.
It is here just to help you learn. Play around with it to see your code changes
affect the result. Scroll down for the next challenge.
Editor
(write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage1_lesson2');
var context = canvas.getContext('2d');
context.fillRect(0, 240, canvas.width, 5);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 2
Try drawing the ground yourself. The only important variable in the
ground drawing is the height from the top of the canvas.
So uncomment out the ground variable and fill in the correct value to
place the ground 260px from the top of the canvas.
In the end your drawing should look like the example provided to the right.
var canvas = document.getElementById('granular_basic_cityscape_stage1_challenge2');
var context = canvas.getContext('2d');
// Set the ground variable properly.
var ground =
context.fillRect(0, ground, canvas.width, 5);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage1_challenge2');
var context = canvas.getContext('2d');
// Set the ground variable properly.
var ground = 260;
context.fillRect(0, ground, canvas.width, 5);
Lesson: Draw a Building
In our drawing a building will be a rectangle. We use context.fillRect() to draw the rectangle,
just as we did above.
In this example, we draw a building that is
160 pixels wide, 240 pixels tall,
and positioned so its
top left corner is at (100, 50)
with it's bottom touching the ground.
Remember: In the context's
coordinate system
the origin (0, 0) is at the top left corner of the canvas.
var canvas = document.getElementById('granular_basic_cityscape_stage1_lesson3');
var context = canvas.getContext('2d');
// THE GROUND
context.fillRect(0, 290, canvas.width, 3);
// THE BUILDING
context.fillRect(100, 290 - 240, 160, 240);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 3
Draw a building (a rectangle) that is
320 pixels wide and 160 pixels tall.
Make sure the bottom of the building rests on the ground
which is
240 pixels from the top.
The building should also be
40 pixels from the left, so the
bottom left corner of the building is covering the x at (40, 240)
and the top right corner is covering the other x, but not covering the red x's.
Remember the you need to draw from the top left corner, not from the ground.
You'll need to calculate the top of the building.
In the end your building should look like the example provided to the right.
Previous Challenge:
View your
code from
Stage
1
Challenge
3
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
1
Challenge
3
Stage
1
Challenge
3
Editor
(write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage1_challenge4');
var context = canvas.getContext('2d');
// YOUR CODE HERE
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage1_challenge4');
var context = canvas.getContext('2d');
var ground = 280;
context.fillStyle = '#999999';
context.fillRect(60, ground - 210, 90, 210);
Lesson: Use Variables to Size and Position a Building
We can use variables to automatically size and position a building.
In this example, we draw a green building (color #228B22) that is twice
as tall as it is wide, and sitting on the ground at (60, 280).
To change the size of the building, all we do is store a different value
in the variable w. The program automatically calculates the height
of the building (assigning it to the variable h) and the y-coordinate
of the top of the building (assigning it to the variable y). These
variables are then used to draw the building.
var canvas = document.getElementById('granular_basic_cityscape_stage1_example5');
var context = canvas.getContext('2d');
var w = 80;
var h = 2 * w;
var y = 280 - h;
context.fillStyle = '#228B22';
context.fillRect(60, y, w, h);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 5
This time
use a variable
to calculate the x and y position of the building and the width and height (
more info about variables
). Setting a variable looks like this:
var x = 9;
Using these variables draw a
gray (#666666) building
that is positioned on the
ground at (60, 240)
and is
80 pixels tall and 200 pixels wide.
In the end your building should look like the example provided to the right.
Previous Challenge:
View your
code from
Stage
1
Challenge
4
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
1
Challenge
4
Stage
1
Challenge
4
Editor
(write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage1_challenge5');
var context = canvas.getContext('2d');
// SET THE VARIABLES PROPERLY
var ground =
var width =
var height =
var x =
var y =
// CODE TO SET THE BUILDING COLOR (#666666) HERE
context.fillRect(x, y, width, height);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage1_challenge5');
var context = canvas.getContext('2d');
var ground = 240;
var width = 200;
var height = 80;
var x = 60;
var y = ground - height;
context.fillStyle = '#666666';
context.fillRect(x, y, width, height);
Challenge 6
For our cityscape, we don't want think about buildings in terms of pixels.
We want to size buildings based on the number of floors they have and the
number of office units on each floor, and then write the program so it
calculates the number of pixels for us.
Each office unit is
16 pixels wide and 16 pixels tall.
The outer walls, roof, and floor of the building are each
4 pixels thick.
Write a program to calculate the size and position of the building for you.
The variable units stores the number of office units on each floor.
The variable floors stores the number of floors in the building.
Then draw a gray (color #999999) building with 10 floors and 8 office units
on each floor sitting on the ground at (120, 280).
The function, drawOffices(x, y, w, h) will draw the offices of the building
if you define w, h, x, and y correctly, where (x, y) are the coordinates of the top left
corner of your building, and w and h are the width and height of your building.
In the end your drawing should look like the example on the right.
Previous Challenge:
View your
code from
Stage
1
Challenge
5
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
1
Challenge
5
Stage
1
Challenge
5
Editor
(write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage1_challenge6');
var context = canvas.getContext('2d');
var ground = 280;
var units = 8;
var floors = 10;
// CALCULATE THE WIDTH "w" HERE
var w =
// CALCULATE THE HEIGHT "h" HERE
var h =
// YOUR CODE TO CALCULATE X AND Y OF THE BUILDING HERE
var x =
var y =
// YOUR CODE TO SET THE COLOR OF THE BUILDING (#999999) HERE
context.fillRect(x, y, w, h);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
var canvas = document.getElementById('flappy_square_stage1_challenge6');
var context = canvas.getContext('2d');
var ground = 280;
var units = 8;
var floors = 10;
var w = (units * 16) + (2 * 4);
var h = (floors * 16) + (2 * 4);
var x = 120;
var y = ground - h;
context.fillStyle = '#999999';
context.fillRect(x, y, w, h);
Lesson: Use a Function to Draw a Building
Drawing a single building takes quite a few steps. We have to calculate
the width and height of the building, and the y-coordinate of the top of
the building. Then we have to set the building color and draw the rectangle.
Once we start drawing in windows, the list of steps will get much longer.
To draw our cityscape, we have to draw dozens of buildings. Typing in all
that code for each building is going to be a lot of work.
If there is a chunk of code that you are going to use over and over again,
putting that code into a function can make your life much easier. Then,
to run the code, all you have to do is call the function by typing one line.
In this example, we create a function that will draw a French flag. Because
we might want to draw a French flag in lots of different places, we make the
x- and y-coordinates of the flag variables.
When we call drawFrenchFlag(30, 100), the 30 is assigned to the variable x
and the 100 is assigned to the variable y inside of the function. Now we can
easily draw as many French flags as we want!
var canvas = document.getElementById('granular_basic_cityscape_stage1_example7');
var context = canvas.getContext('2d');
function drawFrenchFlag(x, y) {
context.fillStyle = '#0055A4';
context.fillRect(x, y, 20, 40);
context.fillStyle = '#FFFFFF';
context.fillRect(x + 20, y, 20, 40);
context.fillStyle = '#EF4135';
context.fillRect(x + 40, y, 20, 40);
}
drawFrenchFlag(30, 100);
drawFrenchFlag(150, 50);
drawFrenchFlag(300, 180);
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 7
Write a function to draw the building from Challenge 6.
The function will be passed four values that it will assign to the following
four variables (called parameters). The first parameter, leftX, is the
x-coordinate of the left side of the building. The second parameter, groundY,
is the y-coordinate of the base of the building. The third parameter, units,
is the number of office units on a floor. The fourth parameter, floors, is
the number of floors in the building.
Then use the function to draw two buildings. The first building will have
12 floors and 8 office units per floor, and it will be sitting on the ground
at (50, 300). The second building will have 18 floors and 6 office units per
floor, and it will be sitting on the ground at (200, 300).
Just like with Challenge 6, both buildings should be gray (color #999999) and each
office should be 16 x 16 width 4 pixels of padding around the whole building.
Previous Challenge:
View your
code from
Stage
1
Challenge
6
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
1
Challenge
6
Stage
1
Challenge
6
Editor
(write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage1_challenge7');
var context = canvas.getContext('2d');
function drawBuilding(leftX, groundY, units, floors) {
// YOUR CODE FOR DRAWING A GENERAL BUILDING HERE
}
// YOUR CODE FOR DRAWING THE TWO SPECIFIC BUILDINGS HERE
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution
Now we are going to clean up our functions a little bit.
A well-written function is like a polite robot who comes over to your house
to do a job, and then leaves everything exactly as it found it. But if you
look at the function used to draw a French flag in the previous example, it
wasn't so tidy. It changed the context.fillStyle to '#EF4135' and never
changed it back.
To make our function more tidy, we are going to call context.save() at the
start of the function and context.restore() at the end of the function.
Calling context.save() saves the state of the context (including the current
context.fillStyle), and context.restore() restores the context to the last
time you saved it.
The other change we are going to make to our function is to use context.translate().
Notice how we had to do some calculations with x and y to figure out the positions
of the white and red rectangles in the French flag? Imagine you had to do the same
calculations for a hundred windows in a building. By using context.translate(), we can
eliminate a lot of that math.
Calling context.translate() moves the origin of the context. If we move the origin
of the context to the top left corner of the flag, then we can draw the rectangles
in the flag as though the flag is positioned at (0, 0). The math is much easier.
However, when using context.translate(), it's even more important to save and then
restore the context. If you think changing the context.fillStyle is rude, changing
the origin of the context and not changing it back is much ruder!
(As an exercise, try to predict what would happen if you took out the context.save()
and context.restore() from the function. You'll have to reset the example and then
refresh the entire page to get everything back to normal.)
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
Challenge 8
This time, write the drawBuilding() function, but using
context.save(), context.translate(), and context.retore()
provided in the function. You'll need to adjust your code from Challenge 7
accordingly.
Then use the drawBuilding function to draw one building with 12 office
units per floor and 6 floors sitting on the ground at (40, 300) and
another building with 10 office units per floor and 15 floors at (280, 300).
Both buildings should be gray (color #999999) and each office
should be 16 x 16 width 4 pixels of padding around the whole building.
Just remember if you call translate but don't restore the state of the context
then the next translation will be relative to the first translation and your
drawing may be off the canvas!
Previous Challenge:
View your
code from
Stage
1
Challenge
7
to use on this challenge.
Code Missing:
You have not yet entered any code in to the previous challenge:
Stage
1
Challenge
7
Stage
1
Challenge
7
Editor
(write code below)
var canvas = document.getElementById('granular_basic_cityscape_stage1_challenge8');
var context = canvas.getContext('2d');
function drawBuilding(leftX, groundY, units, floors) {
context.save();
var width = (units * 16) + (4 * 2);
var height = (floors * 16) + (4 * 2);
// CODE TO TRANSLATE POSITION HERE
// CODE FOR DRAWING A BUILDING HERE
context.restore();
}
// YOUR CODE FOR DRAWING THE TWO BUILDINGS HERE
Message Log
This is a lesson, not a challenge, the code runs automatically.
But change it! Play with it! Click "Run" to see your changes.
Run
Run and Focus Canvas
Reset
Canvas
(your drawing will display here)
A Solution:
Here's the code I wrote to complete this challenge.
View One Possible Solution