Animation
If you are familiar with HTML and CSS (you should be), you would know that you can create animation through CSS animations and keyframes. But before CSS animation was available, website developers would use JavaScript to create animations.
With JavaScript, we are able to change the top, left, right, and bottom value of an element dynamically. Which allows us to determine the position of an element on the screen. The key to making this work, is to change the position
property of an element to either "relative", "absolute" or "fixed" depending which point of reference you want the element to be positioned against.
The best way to explain how JavaScript animation works is through an example. So lets get to it.
To start, we need an element to animate. So why don't we create a square box using a div
element.
<div id="box"></div>
#box {
width: 50px;
height: 50px;
background-color: purple;
position: relative;
}
In order to set the top, left, right, or bottom position of an element, it needs to have one of the three position values, relative, absolute or fixed. Since we don't want to remove the box out of the natural flow of the content, we have set its position to relative.
Now we can use JavaScript to move the element. We first need to use querySelector
to retrieve the element from the DOM, then change its left
CSS property.
var left = 0;
var moveBoxRight = function () {
var box = document.querySelector('#box');
left += 10;
box.style.left = left + 'px';
}
We will have to add a button in the HTML so you can activate the moveBoxRight
function.
<div id="box"></div>
<button onClick="moveBoxRight()">Move Right</button>
Try it on Repl.
Animate With SetInterval
Cool, we managed to move the box to the right every time we click a button, but this isn't exactly animation. To have the browser automatically move the box, we need to enlist the help of a timer.
In JavaScript, there is a method called setInterval
. It accepts a callback function and a number that represents the milliseconds interval it should wait between each execution of the callback function.
We will create a new function that will be passed to setInterval
and executed every 10 milliseconds and change the left property of the box based on a sine graph. The sine graph oscillates between 1 and -1, so we will take the absolute value of the Math.sin
method so it will oscillate between 0 and 1.
var angle = 0;
var oscillateHorizontally = function () {
var box = document.querySelector('#box');
angle += 0.001;
box.style.left = (Math.abs(Math.sin(angle)) * (document.body.clientWidth - 50)) + 'px';
}
We now need to pass oscillateHorizontally
to setInterval
and set the interval period to 100 milliseconds. setInteval
returns an id that identifies the timer created. You need to pass this timer to the clearInterval
method when you want to cancel the interval. A global variable interval
is created to store this timer id.
We create two functions that will start the animation and stop the animation.
var interval;
var startInterval = function () {
interval = window.setInterval(oscillateHorizontally, 10);
}
var stopInterval = function () {
if (interval) {
window.clearInterval(interval);
}
}
We then add two buttons that activate startInterval
and stopInterval
.
<button onClick="startInterval()">Start Interval</button>
<button onClick="stopInterval()">Stop Interval</button>
Important: If we didn't use
setInterval
and instead used a simple loop to update the position of the box, the page would freeze. Browsers do not update the screen when a JavaScript program is running.
Try it on Repl
Animate with RequestAnimationFrame
On a production level website, there is usually a lot of things going on. It might be making requests to servers to get more data. It might be listening for user interactions. In other words, the browser is usually quite busy executing JavaScript.
Creating animation with setInterval
is straight forward, but it doesn't manage browser computational resources very well. If the browser is under heavy load, your setInterval
animation might suffer and become out of sync. Even worse, it might impede the speedy execution of other primary business logic, such as making a credit card payment transaction.
To avoid this, we can use the window.requestAnimationFrame
method to tell the browser we would like to perform an animation. It accepts a callback function which it will call roughly 60 times a second. The function is called at a time where its computational capacity is capable of handling animations. Once the execution is complete, the screen is repainted.
The callback is given a time stamp value in milliseconds so you can use it to calculate the new position of your animated element. Lets create a new oscillate function that we will pass to requestAnimationFrame
to animate our box.
var lastTime = null;
var oscillateByStep = function (time) {
var box = document.querySelector('#box');
if (lastTime !== null) {
angle += (time - lastTime) * 0.001;
}
lastTime = time;
box.style.left = (Math.abs(Math.sin(angle)) * (document.body.clientWidth - 50)) + 'px';
requestAnimationFrame(oscillateByStep);
}
The main difference between this function and the previous one, is that instead of adding a static 0.001 to the angle, we need to base the step on the time input given to us. Unlike setInterval
, callback functions passed to requestAnimationFrame
isn't guaranteed to be called 60 times a second. And the interval between each call isn't always the same either. So the best strategy is to increase our angle based on the time passed since the function was last called. Hence (time - lastTime)
.
Notice the last line of code, requestAnimationFrame(oscillateByStep);
. We need to call requestAnimationFrame
at the end so we can request the next update. If we don't add this line, oscillateByStep
will only be run once.
To start the animation, you just need to execute the following once.
requestAnimationFrame(oscillateByStep);
If we want to be able to stop the animation, we need to modify our oscillateByStep
function slightly. requestAnimationFrame
returns an ID similar to setInterval
which is required when you want to cancel the animation. You need to pass this ID to window.cancelAnimationFrame
to cancel the callback request. Our oscillateByStep
function needs to update a global variable that stores the latest request ID.
var lastTime = null;
var request;
var oscillateByStep = function (time) {
var box = document.querySelector('#box');
if (lastTime !== null) {
angle += (time - lastTime) * 0.0001;
}
lastTime = time;
box.style.left = (Math.abs(Math.sin(angle)) * (document.body.clientWidth - 50)) + 'px';
request = requestAnimationFrame(oscillateByStep);
}
Now we are ready to test this. Add the following two functions.
var startAnimationRequest = function () {
request = requestAnimationFrame(oscillateByStep);
}
var stopAnimationRequest = function () {
if (request) {
window.cancelAnimationFrame(request);
}
}
Add the following buttons as well.
<button onClick="startAnimationRequest()">Start Request</button>
<button onClick="stopAnimationRequest()">Stop Request</button>
Try it on Repl
Activating CSS Animation With JavaScript
If you are planning to add animation on your website, it is recommended to try and do it through CSS animation. With CSS animation, the browser will find the best slot to run the animation when under heavy JavaScript load. We can couple a bit of JavaScript with CSS animation to enjoy the best of both worlds.
A simple CSS animation setup using keyframes that moves the box from left to right and back would look something like this.
@keyframes slide {
0% {
left: 0;
}
100% {
left: calc(100% - 50px);
}
}
.sliding {
animation: 7s infinite alternate slide;
}
All we need to do to start or stop the animation of the box is to add or remove the class name "sliding" from the box element.
var toggleCSSAnimation = function () {
var box = document.querySelector('#box');
box.classList.toggle('sliding');
}
<button onClick="toggleCSSAnimation()">Toggle Sliding</button>
Try it on Repl
There are different ways to do animation in a webpage, so here is a general rule of thumb.
If you are planning to have constantly running animation on your website, try to use CSS animation. It uses less computational power and is easier to manage.
JavaScript controlled step wise animation using setInterval
or requestAnimationFrame
are good for animations that need to be calculated dynamically. Such as a trail of stars that is following the movement of the mouse pointer on the screen.
For one off animations, you can use CSS animation and activate it with JavaScript.