Making Things Move
- Overview
- Moving Strategy
- Questions - How do I stop an animation frame? - How do I stop an interval?
- Examples
- Addition Resources
Overview
Canvas uses immediate rendering meaning that when we draw, it immediately renders on the screen. After we paint something, the canvas forgets about the object and only knows it as pixels. So, there is no object that we can move. Instead, we have to draw it again.
Doing animations on Canvas is like making a stop-motion movie. In every frame, we need to move the objects a little bit to animate them.
It's important to note that when you move an element, you must clear the previous one. The
clearRect()
method is commonly used for this purpose. By specifying the dimensions of the object
to be cleared, you effectively erase its previous position from the canvas.
// Paint a square
ctx.fillRect(1, 0, 2, 2);
// Clear the square's previous location
ctx.clearRect(1, 0, 2, 2);
// Paint the square in its next location
ctx.fillRect(4, 0, 2, 2);
If you're dealing with multiple objects, you can clear the entire canvas using:
ctx.clearRect(0, 0, canvas.height, canvas.width);
Moving Strategy
Both requestAnimationFrame
and setInterval
are used for creating animations or executing
repetitive tasks, but they have some key differences that make requestAnimationFrame
more
suitable for animations and smoother visual effects.
requestAnimationFrame
is a browser API that optimises animations by synchronizing them with the
browser's rendering cycle. This helps in achieving smoother animations and eliminates unnecessary
rendering when the tab is not active.
On the other hand, setInterval
executes a given function at a fixed time interval, irrespective
of the screen refresh rate. This approach might lead to uneven frame rates and less fluid
animations, especially if the interval is too short or if the device is experiencing heavy
processing.
window.requestAnimationFrame()
requestAnimationFrame(callback)
requestAnimationFrame(callback);
let x = 0;
function animate() {
// Clear the canvas
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// Draw a small square at the new x position
ctx.fillRect(x, 0, 1, 1);
// Increment x for the next frame
x += 1;
// Use requestAnimationFrame for the next frame
requestAnimationFrame(animate);
}
animate();
setInterval()
setInterval(callback, delay)
let x = 0;
function animate() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the square at the new position
ctx.fillRect(x, 50, 50, 50);
// Increment x for the next frame
x += 1;
}
setInterval(animate, 16);
Prevent compounding
To prevent the interval from compounding each time the button is clicked, you need to make sure that you're not creating a new interval on every button click. Instead, you should only start a new interval if one isn't already active. You can achieve this by checking if the interval is already running before starting a new one.
let myInterval;
function startInterval() {
if (!myInterval) {
myInterval = setInterval(() => {
// Your interval logic here
}, 1000);
}
}
function stopInterval() {
if (myInterval) {
clearInterval(myInterval);
myInterval = undefined; // Reset the interval ID
}
}
In this example, the interval is only started when the button is clicked if there's no active interval (intervalId
is not set). This prevents the interval from speeding up on every button click and ensures that only one interval is running at a time.
Questions
How do I stop an animation frame?
To stop an animation that is running using the requestAnimationFrame
function, you can use the
cancelAnimationFrame
function.
let myAnimation; // Declare variable for reference
function animate() {
// Your animation logic here
// Request the next animation frame
myAnimation = requestAnimationFrame(animate);
}
function stopAnimation() {
cancelAnimationFrame(myAnimation); // Cancel the animation frame request
}
How do I stop an interval?
To stop an interval you can use the clearInterval
function.
let myInterval = setInterval(() => {
// interval logic here
}, 1000);
clearInterval(myInterval);
Examples
The following examples assume you already have the canvas and context set up.
Start and stop animation frames
requestAnimationFrame
and cancelAnimationFrame
Starts and stops from current x
and y
position.
let myAnimation: number; // Declare variable for reference
let x = 0;
let speed = 0.1;
let animationRunning = false; // Track whether animation is already running
function startAnimation() {
if (!animationRunning) {
animationRunning = true; // Set animation as running
animate();
}
}
function animate() {
// Clear the canvas
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// Draw a shape at the new x position
ctx.fillRect(x, 1, 1, 1);
// Increment x based on the speed for the next frame
x += speed;
// Use requestAnimationFrame for the next frame
myAnimation = requestAnimationFrame(animate);
}
function stopAnimation() {
cancelAnimationFrame(myAnimation);
animationRunning = false; // Reset animation status
}
Compounding speed
let myAnimation: number; // Declare variable for reference
let x = 0;
let speed = .1;
function startAnimation() {
// Clear the canvas
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// Draw a shape at the new x position
ctx.fillRect(x, 1, 1, 1);
// Increment x based on the speed for the next frame
x += speed;
// Use requestAnimationFrame for the next frame
myAnimation = requestAnimationFrame(startAnimation);
}
function stopAnimation() {
cancelAnimationFrame(myAnimation);
}
setInterval
and clearInterval
Starts and stops from current x
and y
position.
let myInterval: number | undefined; // Declare variable for reference
let x = 0;
let step = 1; // distance each step will take
function startInterval() {
// prevent interval from compounding
if (!myInterval) {
myInterval = setInterval(() => {
// Clear the canvas
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// Draw a shape at the new x position
ctx.fillRect(x, 1, 1, 1);
// increment x by the step distance
x += step;
}, 500)
}
}
function stopInterval() {
if (myInterval) {
clearInterval(myInterval);
myInterval = undefined; // Reset the interval ID
}
}