Introduction
Nowadays, more and more animation is applied on Front-end. It’s interesting to understand how JavaScript controls the animation.
In this article, I will talk about:
- what is the animation in the browser
- how to build a timeline
- how to build an animation
- how to control a timeline (pause / resume)
Animation in browser
Animation is a method in which frames are manipulated to appear as moving images. In browser, we can write a function to move images, and call this function repeatedly to achieve animation.
There are 3 ways.
setInterval
: It can repeatedly call a function or executes a code snippet, with a fixed time delay between each call.
Usually, the browser shows 60 frames per second. Therefore the interval is set as 16ms (16ms * 60 = 960 < 1000ms).
let timerId = setInterval(() => alert('tick'), 16);
setTimeout
: a better way to achieve animation. It’s more flexible.
let timerId = setTimeout(function tick() {
alert('tick');
timerId = setTimeout(tick, 16);
}, 16);
There are 2 advantages of setTimeout
:
- Delay (16ms) can be controlled dynamically.
- Delay (16ms) is applied precisely.
To control the delay:
let delay = 16;
let timerId = setTimeout(function tick() {
alert('tick');
if (some-condition) delay += 16; // delay is updated
timerId = setTimeout(tick, delay);
}, delay);
setInterval
fixes the interval as 16ms, regardless the time of executing the function. That means the real time between two function is less than 16ms. However, setTimeout
ensures the interval is exactly 16ms.
(more details is presented here)
requestAnimationFrame
: this function calculates the interval between frames automatically. I will use this one in this article.
let tick = () => { requestAnimationFrame(tick) };cancelAnimationFrame(tick); // cancel the tick animation
Timeline
Timeline
is the class which controls the animation. Its attributes are:
tick
&tickHandler
: the functions which trigger the animationtimelineStart
: the start time of the timeline (it’s NOT the start time of animation)pauseStart
&pausedTime
: the start time of the pause & the duration of the pause in the timelineanimations
: all the animation in the timeline. They are saved in aSet
.animationStart
: the time of registration of each animation in timeline. They are saved in aMap
. The key isanimation
, value is the time.
These attributes must be private. We don’t want they are changed by outside accidentally. Unfortunately, JavaScript doesn’t have the private
keyword. But, we can useSymbol
to create a private key since Symbol
is unique. Even if two Symbol have the same name, they are different. For exemple, we create a private animations
:
const ANIMATIONS = Symbol('animations');export class Timeline {
constructor() { this[ANIMATIONS] = new Set(); }
}
Timeline
has several methods:
start
: start the timeline, and start the animation.pause
: note thepauseStart
time, and cancel the current animationresume
: calculate the duration of pause, and start the animationadd
: register an animation, and save the time of registration.
It’s easy to cancel the animation. Since I use requireAnimationFrame
:
this[TICK_HANDLER] = requestAnimationFrame(this[TICK]);
cancelAnimationFrame(this[TICK_HANDLER]);
The tricky part is to start one. The tick
function deals with it.
First, it marks the start time as now
. Then, for each animation, we need to calculate the time of animation:
- If the animation is registered before the timeline start, then the time of animation is
now — timelineStart
- If the animation is registered after the timeline start, then the time of animation is
now — animationStart
Next, the pausedTime
and animation delay should be removed.
Finally, this time is passed to the animation, and animation is executed.
Animation
In browser, the animation is created by CSS. In the exemple, sliding block controlled by mouse, the animation is controlled by transform
transform: translateX(20px)
That means move the block 20px right.
We need to create an Animation class. It receives the time of animation, and calculates the position of the block. The method receive(time)
works on this.
Before presenting receive
method, let’s see the attributes of Animation.
object
&property
: the CSS property of HTML element we want to control. In this example, it’stransform
template
: the function that returns the value ofproperty
, for example,pos => `translateX(${pos})`
startValue
&endValue
&duration
: these are the parameters of animation. Combine these value with time, the position of block can be calculated.delay
: the time of delay, used in timelinetimingfn
: The timing function controls the effect of animation, such as linear, ease, ease-in, …
Well, receive(time)
calculates the range of animation by endValue - startValue
, and the progress of animation by timingFn(time/duration)
. The template
accept startValue + range * progress
, and returns the position.
Timeline control
Since Timeline and Animation have been created, animation can be controlled now.
First, let’s set a simple HTML, create a block.
Next, in JavaScript file, I created a timeline and an animation. then, I bound it with mouseover
and mouseout
event. Finally, I launched the timeline and add the animation to it.
In fact, the timing function is Cubic Bezier Curve, and I used the existed codes.
Conclusion
In this article, I presented how to control the animation using JavaScript. The main idea is create a timeline, which can maintain several animation, and the animation can modify the CSS of HTML element. The most important part of this little project is how to calculate the position of HTML element with the animation time.
If you want to try it, don’t forget pack the files. You may need following setup.
npm initnpm install --save-dev webpack webpack-cli webpack-dev-servernpm install --save-dev babel-loader @babel/core @babel/preset-env
and, the webpack configuration:
Finally, run webpack serve
, enjoy :)