Hi everyone on our 4th lesson of the course “Canvas Basics Guide”. Today we’re going to add some animation on canvas using javascript
.
Intro
What do we know about animation? How does it work? Ok, basically it’s just order of images when every next image different from the previous. These images are changed with some frequency, so it creates some effect of moving. Also, we know about fps
(frames per second) - it’s this frequency what we’re talking about. So, let’s create some animation on canvas.
Draw an image
Firstly, we should output the image on the canvas. We learned how to do it in the previous lesson. Let’s prepare our canvas and draw a ball, which we’ll animate in the next steps:
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const cWidth = 500
const cHeight = 500
const bImg = new Image()
bImg.src = './ball.png'
const bWidth = 100
const bHeight = 100
bImg.onload = () => {
ctx.drawImage(bImg, 150, 150, bWidth, bHeight)
}
We’re getting:
setInterval for animation
Now, we have to re-render a canvas with the new ball’s position. Let’s increase y
position to simulate the falling effect, and call the drawing method with a new position:
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const cWidth = 500
const cHeight = 500
const bImg = new Image()
bImg.src = './ball.png'
const bWidth = 100
const bHeight = 100
bImg.onload = () => {
ctx.drawImage(bImg, 150, 150, bWidth, bHeight)
}
const render = () => { currY += 10 ctx.drawImage(bImg, 150, currY, bWidth, bHeight)}
const delay = 16setInterval(render, delay)
Well, is it what you’ve expected?
Ok, don’t worry, you just have to clear canvas each time before repainting, so after some fixes:
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const cWidth = 500
const cHeight = 500
const bImg = new Image()
bImg.src = './ball.png'
const bWidth = 100
const bHeight = 100
bImg.onload = () => {
ctx.drawImage(bImg, 150, 150, bWidth, bHeight)
}
const render = () => {
ctx.clearRect(0, 0, cWidth, cHeight)
currY += 10
ctx.drawImage(bImg, 150, currY, bWidth, bHeight)
}
const delay = 16
setInterval(render, delay)
We’re getting basic animation:
But, why our ball falling through the bottom, let’s fix that by adding speed variable and checking current position:
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const cWidth = 500
const cHeight = 500
const bImg = new Image()
bImg.src = './ball.png'
const bWidth = 100
const bHeight = 100
bImg.onload = () => {
ctx.drawImage(bImg, 150, 150, bWidth, bHeight)
}
// initial speed
let bSpeed = 10
const MAX_BOTTOM_POS = cHeight - bHeight
const render = () => {
ctx.clearRect(0, 0, cWidth, cHeight)
currY += bSpeed
if (currY >= MAX_BOTTOM_POS) { currY = MAX_BOTTOM_POS bSpeed = 0 } ctx.drawImage(bImg, 150, currY, bWidth, bHeight)
}
const delay = 16
setInterval(render, delay)
So, now the ball stops at the bottom:
Playing around
But it’s not the end, by adding some magic (physics), we can get a realistic ball with acceleration and reflection from the bottom. We’ll skip all theory to simplify this tutorial (it’s not our main topic), but this easier than you could guess:
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const cWidth = 500
const cHeight = 500
const bImg = new Image()
bImg.src = './ball.png'
const bWidth = 100
const bHeight = 100
bImg.onload = () => {
ctx.drawImage(bImg, 150, 150, bWidth, bHeight)
}
let currX = 150
let currY = 150
// initial speed
let bSpeed = 0
// acceleration
let a = 1let initialSpeed = 0// time
let time = 0
// direction down
let d = -1
const MIN_TOP_POS = 150const MAX_BOTTOM_POS = cHeight - bHeight
const render = () => {
ctx.clearRect(0, 0, cWidth, cHeight)
time += delay bSpeed = initialSpeed + d * a * (time / 1000) const shift = d * bSpeed * delay currY += shift
// bottom
if (currY >= MAX_BOTTOM_POS) { currY = MAX_BOTTOM_POS - shift d = 1 initialSpeed = bSpeed time = 0 } else if (currY <= MIN_TOP_POS) { currY = MIN_TOP_POS d = -1 initialSpeed = 0 time = 0 }
ctx.drawImage(bImg, 150, currY, bWidth, bHeight)
}
const delay = 16
setInterval(render, delay)
Realistic falling (with acceleration):
Extra work: How to rotate the image on the canvas
What about rotation? It’s can be not so clear. Indeed For rotation image on the canvas, you should rotate your canvas by setting context point (o, o) to the ball’s center and rotation canvas by this point, after that we have to draw image using new coordinates (as well as your context point will be at ball center for drawing the ball you have to shift x
, y
by left and top on half of the ball). Before and after that we’ll use save
and restore
methods for returning context in the normal mode (to set context point at (0, 0)):
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const cWidth = 500
const cHeight = 500
const bImg = new Image()
bImg.src = './ball.png'
const bWidth = 100
const bHeight = 100
bImg.onload = () => {
ctx.drawImage(bImg, 150, 150, bWidth, bHeight)
}
let currX = 150
let currY = 150
// initial speed
let bSpeed = 0
let rotationSpeed = 0.3let rotationDegs = 0
// acceleration
let a = 1
let initialSpeed = 0
// time
let time = 0
// direction down
let d = -1
const MIN_TOP_POS = 150
const MAX_BOTTOM_POS = cHeight - bHeight
const render = () => {
ctx.clearRect(0, 0, cWidth, cHeight)
time += delay
bSpeed = initialSpeed + d * a * (time / 1000)
const shift = d * bSpeed * delay
currY += shift
rotationDegs += shift * rotationSpeed // bottom
if (currY >= MAX_BOTTOM_POS) {
currY = MAX_BOTTOM_POS - shift
d = 1
initialSpeed = bSpeed
time = 0
} else if (currY <= MIN_TOP_POS) {
currY = MIN_TOP_POS
d = -1
initialSpeed = 0
time = 0
}
ctx.save() // move to the center of the ball
ctx.translate(currX + bWidth / 2, currY + bHeight / 2) // rotate the canvas to the specified degrees
ctx.rotate(rotationDegs * Math.PI / 180) // since the context is rotated, the image will be rotated also
ctx.drawImage(bImg, -bWidth / 2, -bHeight / 2, bWidth, bHeight) ctx.restore()}
const delay = 16
setInterval(render, delay)
Falling with rotation:
Conclusions
We’ve learned how to animate objects on the canvas. See you in the next lesson, where we’ll consider advanced animation (requestAnimationFrame vs setInterval, sprite animation).