To ensure canvas dimensions are rendered properly they have to be set in pixels. Best way to do this is to grab the dimensions of the browser window and set them to canvas.
index.html
<canvas id="main-canvas"></canvas>
style.css
canvas {
border: 3px solid red;
visibility: hidden;
}
body {
margin: 0; /* This removes the margin of the browser's default styling */
overflow: hidden; /* This prevents scroll overflow showing */
}
index.js
// SET CANVAS DIMENSIONS
// When page loads, canvas and context are created, and
// dimensions of the canvas are set
window.addEventListener('load', () => {
const canvas = document.getElementById('main-canvas');
const context = canvas.getContext('2d');
canvas.height = document.body.clientWidth;
canvas.width = document.body.clientHeight;
canvas.style.visibility = 'visible';
});
When doing animations on canvas
, we need to create a set of frames that the canvas will render in a sequence to create the illusion of an animation.
When making canvas animations there are 4 steps we can take
- Save canvas state - snapshot state of what was shown Save the current state of the drawing (colors, styles , etc) each time a frame is drawn (current frame).
- Clear the canvas
We need to clear any shapes that have been drawn previously using the
clearRect()
method. - Restore the canvas state If you’ve saved the state, restore it before drawing a new frame.
- Draw animated shapes The step where you do the actual frame rendering/drawing.
Every time the save()
method is called, the current drawing state is pushed onto the stack.
A drawing state consists of:
-
The transformations that have been applied (i.e. translate, rotate and scale).
-
The current values of the following attributes:
strokeStyle
,fillStyle
,globalAlpha
,lineWidth
,lineCap
,lineJoin
,miterLimit
,lineDashOffset
,shadowOffsetX
,shadowOffsetY
,shadowBlur
,shadowColor
,globalCompositeOperation
,font
,textAlign
,textBaseline
,direction
,imageSmoothingEnabled
.
You can call the save()
method as many times as you like.
When the restore()
method is called, the last saved state is popped off the stack and all saved settings are restored and applied.
save()
is equivalent topush
.restore()
is equivalent topop
.
setInterval(method, delay) // Starts repeatedly executing the function specified by method every delay milliseconds.
setTimeout(method, delay) // Executes the function specified by function in delay milliseconds.
Previous example of using the setTimeout
with the fireball animation:
-
requireAnimationFrame()
Calls the given callback function before the next rendering of frame on the screen). -
It runs the given callback around 60 times per second, so it enables smooth rendering.
-
requestAnimationFrame()
returns an id and can be canceled using thecancelAnimationFrame(globalID);
Let's see how requestAnimationFrame()
is used.
We will work with rendering DOM elements. LAter we will do the real example of rendering canvas elements.
-
requestAnimaionFrame()
creates 60 frames per second. -
By changing the element on each frame we create an animation.
-
To move the element we have to change it's position. We need to increase or decrease the value of element's
x
ory
position. -
To move a canvas element faster with
requestAnimationFrame(callback)
, we just need to increase or decrease thex
ory
values more on eachcallback
.
EXAMPLE
index.html
<canvas id="main-canvas" width="700" height="900"></canvas>
<button>START</button>
index.js
const canvas = document.getElementById("main-canvas");
const ctx = canvas.getContext("2d");
const button = document.querySelector('button');
ctx.fillStyle = "red"; // set fill style for all of the squares
// context.fillRect( x, y , width, height);
ctx.fillRect(100, 0, 50, 50);
ctx.fillRect(300, 0, 50, 50);
ctx.fillRect(500, 0, 50, 50);
let speed1 = 0;
let speed2 = 0;
let speed3 = 0;
function clearCanvas() {
// clearRect( startAt-x, startAt-y , width-span, height-span);
ctx.clearRect(0, 0, 700, 900);
}
function updateCanvas(){
speed1 += 1; // slowest - 1px per frame
speed2 += 2; // faster - 2px per frame
speed3 += 3; // fastest - 3px per frame
// clear the previous frame of elements before printing new one
clearCanvas();
ctx.fillRect(100, speed1, 50, 50);
ctx.fillRect(300, speed2, 50, 50);
ctx.fillRect(500, speed3, 50, 50);
// once all 3 squares are painted, call function again and paint again
window.requestAnimationFrame(updateCanvas);
}
// START THE LOOP VIA:
// INITIAL CALL to updateCanvas
// requestAnimationFrame - calls the callback 60 times per second
// window.requestAnimationFrame(updateCanvas);
// OR BY EVENT LISTENER TRIGGERING updateCanvas
button.addEventListener('click', function() {
window.requestAnimationFrame(updateCanvas);
});
Let's go through the code together step by step
index.html
<canvas id="canvas" height="500" width="700"></canvas>
style.css
canvas {
border: 1px solid red;
background: black;
}
index.js
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = 'white';
ctx.font = '18px serif';
// ghost object
let ghost = {
x: 25,
y: 25,
moveUp: function() { this.y -= 25 },
moveDown: function() { this.y += 25 },
moveLeft: function() { this.x -= 25 },
moveRight: function() { this.x += 25 },
}
document.addEventListener('keydown', function (e) {
console.log('KEYDOWN CODE ->', e.keyCode );
switch (e.keyCode) {
case 38:
ghost.moveUp();
console.log('up',ghost);
break;
case 40: ghost.moveDown();
console.log('down', ghost);
break;
case 37: ghost.moveLeft();
console.log('left', ghost);
break;
case 39: ghost.moveRight();
console.log('right', ghost);
break;
updateCanvas();
}
function draw (ghost) {
let img = document.createElement('img');
img.src = "https://media.giphy.com/media/Qr8JE9Hvi7ave/200.gif";
img.onload = function() {
// drawImage(imgObj, position-x, position-y, width, height);
ctx.drawImage(img, ghost.x, ghost.y, 50, 50);
}
}
function updateCanvas() {
ctx.clearRect(0, 0, 1500,1700);
// fillText(text, position-x, position-y, maxWidth);
ctx.fillText("Ghost_x: " + ghost.x, 580, 40);
ctx.fillText("Ghost_y: " + ghost.y, 580, 60);
draw(ghost)
}
// initial call to create the ghost on the canvas
updateCanvas();
We may want to restart everything, putting the ghost on it's initial position.
Let´s do it using the spacebar
key.
Solution
case 32: // Space
console.log('back to start', ghost);
ghost.x = 50;
ghost.y = 50;
break;
-
When rendering a 2d game, we usually need a background image that moves while our character or element is moving through the
canvas
. -
To achieve this effect you will need to find a image that can loop and whose dimensions match your use. It may be a bit tricky to set it because you will have to adjust the image so that there are no connections of one image to another showing.
Example - Using Canvas
Using CSS
Another way to do this is to provide a background image on the container div
that is holding the canvas and add the css animation to loop the background.