JS.Interact.Animate.UseArraysandLoopstoAnimatemany.ex1
canvas {
border: 1px solid black;
}
// useful to have them as global variables
var canvas, ctx, w, h;
// an empty array!
var balls = [];
var player = {
x:10,
y:10,
width:20,
height:20,
color:'red'
}
window.onload = function init() {
// called AFTER the page has been loaded
canvas = document.querySelector("#myCanvas");
// often useful
w = canvas.width;
h = canvas.height;
// important, we will draw with this object
ctx = canvas.getContext('2d');
// create 10 balls
balls = createBalls(10);
// ready to go !
mainLoop();
};
function mainLoop() {
// 1 - clear the canvas
ctx.clearRect(0, 0, w, h);
// draw the ball and the player
drawFilledRectangle(player);
drawAllBalls(balls);
// animate the ball that is bouncing all over the walls
moveAllBalls(balls)
// ask for a new animation frame
requestAnimationFrame(mainLoop);
}
function createBalls(n) {
// empty array
var ballArray = [];
// create n balls
for(var i=0; i < n; i++) {
var b = {
x:w/2,
y:h/2,
radius: 5 + 30 * Math.random(), // between 5 and 35
speedX: -5 + 10 * Math.random(), // between -5 and + 5
speedY: -5 + 10 * Math.random(), // between -5 and + 5
color:getARandomColor(),
}
// add ball b to the array
ballArray.push(b);
}
// returns the array full of randomly created balls
return ballArray;
}
function getARandomColor() {
var colors = ['red', 'blue', 'cyan', 'purple', 'pink', 'green', 'yellow'];
// a value between 0 and color.length-1
// Math.round = rounded value
// Math.random() a value between 0 and 1
var colorIndex = Math.round((colors.length-1)*Math.random());
var c = colors[colorIndex];
// return the random color
return c;
}
function drawAllBalls(ballArray) {
ballArray.forEach(function(b) {
drawFilledCircle(b);
});
}
function moveAllBalls(ballArray) {
// iterate on all balls in array
ballArray.forEach(function(b) {
// b is the current ball in the array
b.x += b.speedX;
b.y += b.speedY;
testCollisionBallWithWalls(b);
});
}
function testCollisionBallWithWalls(b) {
// COLLISION WITH VERTICAL WALLS ?
if((b.x + b.radius) > w) {
// the ball hit the right wall
// change horizontal direction
b.speedX = -b.speedX;
// put the ball at the collision point
b.x = w - b.radius;
} else if((b.x -b.radius) < 0) {
// the ball hit the left wall
// change horizontal direction
b.speedX = -b.speedX;
// put the ball at the collision point
b.x = b.radius;
}
// COLLISIONS WTH HORIZONTAL WALLS ?
// Not in the else as the ball can touch both
// vertical and horizontal walls in corners
if((b.y + b.radius) > h) {
// the ball hit the right wall
// change horizontal direction
b.speedY = -b.speedY;
// put the ball at the collision point
b.y = h - b.radius;
} else if((b.y -b.radius) < 0) {
// the ball hit the left wall
// change horizontal direction
b.speedY = -b.speedY;
// put the ball at the collision point
b.Y = b.radius;
}
}
function drawFilledRectangle(r) {
// GOOD practice: save the context, use 2D trasnformations
ctx.save();
// translate the coordinate system, draw relative to it
ctx.translate(r.x, r.y);
ctx.fillStyle = r.color;
// (0, 0) is the top left corner of the monster.
ctx.fillRect(0, 0, r.width, r.height);
// GOOD practice: restore the context
ctx.restore();
}
function drawFilledCircle(c) {
// GOOD practice: save the context, use 2D trasnformations
ctx.save();
// translate the coordinate system, draw relative to it
ctx.translate(c.x, c.y);
ctx.fillStyle = c.color;
// (0, 0) is the top left corner of the monster.
ctx.beginPath();
ctx.arc(0, 0, c.radius, 0, 2*Math.PI);
ctx.fill();
// GOOD practice: restore the context
ctx.restore();
}
<!DOCTYPE html>
<html lang="en">
<head>
<!----
Let's look at the new functions we've added:
createBalls(numberOfBalls), returns an array of balls:
function createBalls(n) {
// empty array
var ballArray = [];
// create n balls
for(var i=0; i < n; i++) {
var b = {
x:w/2,
y:h/2,
radius: 5 + 30 * Math.random(), // between 5 and 35
speedX: -5 + 10 * Math.random(), // between -5 and + 5
speedY: -5 + 10 * Math.random(), // between -5 and + 5
color: getARandomColor(),
}
// add ball b to the array
ballArray.push(b);
}
// returns the array full of randomly created balls
return ballArray;
}
Explanations:
Line 3: we declare an empty array that will contain the balls,
Lines 7-14: we create a new ball object with random values. Note the use of Math.random(), a predefined JavaScript function that returns a decimal value between 0 and 1. We call another function named getARandomColor() that returns a color taken randomly.
Line 16: we add the newly created ball b to the array,
Line 19: we return the array to the caller.
The getARandomColor function
function getARandomColor() {
var colors = ['red', 'blue', 'cyan', 'purple',
'pink', 'green', 'yellow'];
// a value between 0 and color.length-1
// Math.round = rounded value
// Math.random() a value between 0 and 1
var colorIndex = Math.round((colors.length-1)*Math.random());
var c = colors[colorIndex];
// return the random color
return c;
}
Explanations:
Line 2: in this function, we use an array of random color names named colors (you can go on the codePen example and change these colors or add new ones).
Line 7: then we compute an index with a random value between 0 and colors.length-1. Remember that in an array of n elements, the index of the first is always 0 and the index of the last one is always equal to the length of the array -1. For example: var myArray = ['red', 'blue', 'green'], red is at index 0, green at index 2, while myArray.length = 3, the number of elements in the array.
Lines 8 and 11: once we get a random index in the correct range, we can return the corresponding color.
Functions drawAllBalls and moveAllBalls:
function drawAllBalls(ballArray) {
ballArray.forEach(function(b) {
drawFilledCircle(b);
});
}
function moveAllBalls(ballArray) {
// iterate on all balls in array
ballArray.forEach(function(b) {
// b is the current ball in the array
b.x += b.speedX;
b.y += b.speedY;
testCollisionBallWithWalls(b);
});
}
Explanations:
These two functions use an iterator on the array of balls (using the forEach method that looked the best fit here). The code inside the iterator is the same as in the previous example. We did not have to modify the testCollisionBallWithWalls code, for example.
-->
<meta charset="utf-8">
<title>Draw a monster in a canvas</title>
</head>
<body>
<canvas id="myCanvas" width="400" height="400"></canvas>
</body>
</html>