JavaScript Animations

JavaScript Animations (JavaScript动画)

As you have already learned, CSS animations are used for making simple animations. All the things that CSS can’t handle, you can handle using the JavaScript animations. (正如您已经了解的那样, CSS动画用于制作简单的动画。CSS无法处理的所有事情,都可以使用JavaScript动画进行处理。)

For example, you can use JavaScript animations when you want to move along a complex path, with a timing function not similar to the Bezier curve, either an animation on a canvas. (例如,当您想沿着复杂路径移动时,可以使用JavaScript动画,其定时功能与贝塞尔曲线不同,可以是画布上的动画。)

SetInterval

SetInterval

An animation can be performed as an order of frames. (动画可以按帧顺序执行。)

For example, if you change style.left from 0px to 100px, the element will be moved. But when you increase it in setInterval, changing by 2px with a little delay, such as 50 times per second, it will look smooth. Here is the pseudo-code:

let timer = setInterval(function () {
 if (animation complete) clearInterval(timer);
 else increase style.left by 3 px
}, 20); // changes to 3px every 20 ms, about 50 frames per second

A more complex example of the animation will look as follows:

<!DOCTYPE html>
<html>
 <head>
   <title>Title of the Document</title>
   <style>
     img {
       position: relative;
       cursor: pointer;
       width: 300px;
       height: 200px;
     }
   </style>
 </head>
 <body>
   <img id="imgId" src="https://ru.w3cdoc.com/uploads/media/default/0001/05/2c669e35c4c5867094de4bed65034d0cac696df5.png" alt="JS" />
   <script>
     imgId.onclick = function() {
       let start = Date.now();
       let timer = setInterval(function() {
         let timePassed = Date.now() - start;
         imgId.style.left = timePassed / 10 + 'px';
         if(timePassed > 3000) clearInterval(timer);
       }, 20);
     }
   </script>
 </body>
</html>

RequestAnimationFrame

RequestAnimationFrame

Let’s say multiple animations are running simultaneously. In the event of calling them separately, although each of them has setInterval(…, 20), the browser will have to repaint more often than 20ms. (假设多个动画同时运行。如果分别调用它们,虽然每个都设置了Interval (…, 20) ,但浏览器必须重新绘制的频率超过20毫秒。)

The reason is that their starting time is different. There are no aligned intervals. Hence, there are different runs within 20ms. (原因是他们的开始时间不同。没有对齐的间隔。因此, 20毫秒内有不同的运行。)

It is demonstrated in the example below:

setInterval(function () {
 animation1();
 animation2();
 animation3();
}, 30)

And, that is lighter than three independent calls, like here:

setInterval(animation1, 30); // independent animations
setInterval(animation2, 30); // in different places of the script
setInterval(animation3, 30);

Another crucial point to keep in mind: at the times when the CPU gets overloaded or there are other reasons, it is not necessary to run every 30ms. If you wonder how you can know about it in JavaScript, then you need to address the requestAnimationFrame function. It can help you solve all these issues and even more.

The syntax of requestAnimationFrame is the following:

let requestId = requestAnimationFrame(callback);

It schedules the callback function to run in the nearest time when the browser intends to perform an animation. (它将调度回调函数在浏览器打算执行动画的最近时间运行。)

Making changes in the elements of the callback will get them grouped with other requestAnimationFrame and CSS animations. (对回调的元素进行更改将使其与其他requestAnimationFrame和CSS动画分组。)

You can use the returned value requestId for canceling the call, like this:

// cancel scheduled callback execution
cancelAnimationFrame(requestId);

One argument is received by the callback - the time passed from the start of the page load in microseconds. It can also be obtained with the performance.now() call. (回调接收到一个参数-从页面加载开始经过的时间(以微秒为单位)。也可以通过performance.now ()调用获得。)

As a rule, the callback runs soon, no matter the CPU is overloaded, the battery of the laptop is almost discharged, and so on. The time between the first ten runs for requestAnimationFrame is demonstrated in the code below:

let prev = performance.now();
let times = 0;
requestAnimationFrame(function measure(time) {
 console.log("beforeEnd", Math.floor(time - prev) + " ");
 prev = time;
 if (times++ < 10){
   requestAnimationFrame(measure);
 }
})

As a rule, the time is 10-20 ms. (通常,时间为10-20毫秒。)

Structured Animation

Structured Animation (结构化动画)

If we try to make a more universal animation, based on requestAnimationFrame, it will look as follows:

function animate({
 timing,
(计时器)
 draw,
(v.吸引 ,开立 ,提存,提取,支领)
 duration
}) {
 let start = performance.now();
 requestAnimationFrame(function animate(time) {
   // fractionOfTime goes from 0 to 1
(//fractionOfTime从0到1)
   let fractionOfTime = (time - start) / duration;
   if (fractionOfTime > 1) {
     fractionOfTime = 1;
   }
   // calculate the current state of the animation
(//计算动画的当前状态)
   let progress = timing(fractionOfTime);
   draw(progress); // draw it 
   if (fractionOfTime < 1) {
     requestAnimationFrame(animate);
   }
 });
}

The animate function receives three parameters, essentially describing the animation. (动画函数接收三个参数,基本上描述动画。)

Those parameters are:

function linear(timeFraction) {
 return timeFraction;
}
function draw(progress) {
 img.style.left = progress + 'px';
}

Let’s try to animate the width element from 0 to 100% applying the function above. (让我们尝试应用上述函数,将width元素从0设置为100%。)

The demo will look like this:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
   <style>
     progress {
       width: 30%;
     }
   </style>
 </head>
 <body>
   <!--animation script-->
   <script>
     function animate({
       timing, draw, duration
(时间、平局、持续时间)
     }) {
       let start = performance.now();
       requestAnimationFrame(function animate(time) {
         let fractionOfTime = (time - start) / duration;
         if(fractionOfTime > 1) {
           fractionOfTime = 1;
         }
         let progress = timing(fractionOfTime);
         draw(progress);
         if(fractionOfTime < 1) {
           requestAnimationFrame(animate);
         }
       });
     }
   </script>
   <progress id="elem"></progress>
   <script>
     elem.onclick = function() {
       animate({
         duration: 500,
         timing: function(fractionOfTime) {
           return fractionOfTime;
         },
         draw: function(progress) {
           elem.style.width = progress * 100 + '%';
         }
       });
     };
   </script>
 </body>
</html>

In contrast with CSS animations, any timing function and any drawing function can be made here. Moreover, the timing function here is not limited by the Bezier curves. The draw may go beyond properties, make new elements for the animation. (与CSS动画相比,可以在此处制作任何定时功能和任何绘图功能。此外,这里的定时函数不受贝塞尔曲线的限制。绘制可以超越属性,为动画制作新元素。)

Timing Functions

Timing Functions (计时功能)

We have already explored the simple, linear timing. Now, we are going to show you movement animations with different timing functions and their usage. To be more precise, we will overview the functions such as the power of n, the arc, bow shooting, the bounce, and the elastic animation. (我们已经探索了简单的线性计时。 现在,我们将向您展示具有不同定时功能的运动动画及其用法。 更准确地说,我们将概述n的幂、弧、弓射、反弹和弹性动画等功能。)

Bounce

Bouncing is quite common action. You can see it in your everyday life. For instance, if you drop a ball, it will bounce back several times and then will stop. The bounce function acts like that, but in reverse order, the bouncing will start at once. Several unique coefficients are used for it, like this:

function bounce(fractionOfTime) {
 for (let a = 0, b = 1, result; 1; a += b, b /= 2) {
   if (fractionOfTime >= (7 - 4 * a) / 11) {
     return -Math.pow((11 - 6 * a - 11 * fractionOfTime) / 4, 2) + Math.pow(b, 2)
(return -Math.pow ((11 - 6 * a - 11 * fractionOfTime)/4, 2) + Math.pow (b, 2))
   }
 }
}

EaseOut

The timing function is put into a wrapper timingEaseOut in the “easeOut” mode:

timingEaseOut(fractionOfTime) = 1 - time(1 - fractionOfTime);

That is, there is a makeEaseOut function, which takes a regular timing function, returning the wrapper around it, like this:

// taking a timing function, returning the transformed variant
function makeEaseOut(time) {
 return function (fractionOfTime) {
   return 1 - time(1 - fractionOfTime);
 }
}

Let’s try to take the bounce function and use it:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
   <script src="https://js.cx/libs/animate.js"></script>
   <style>
     #brick {
       width: 40px;
       height: 25px;
       background: blue;
       position: relative;
       cursor: pointer;
     }
     #path {
       outline: 1px solid #E8C48E;
       width: 540px;
       height: 25px;
     }
   </style>
 </head>
 <body>
   <div id="path">
     <div id="brick"></div>
   </div>
   <script>
     function makeEaseOut(time) {
       return function(fractionOfTime) {
         return 1 - time(1 - fractionOfTime);
       }
     }
     function bounce(fractionOfTime) {
       for(let a = 0, b = 1, result; 1; a += b, b /= 2) {
         if(fractionOfTime >= (7 - 4 * a) / 11) {
           return -Math.pow((11 - 6 * a - 11 * fractionOfTime) / 4, 2) + Math.pow(b, 2)
(return -Math.pow ((11 - 6 * a - 11 * fractionOfTime)/4, 2) + Math.pow (b, 2))
         }
       }
     }
     let bounceEaseOut = makeEaseOut(bounce);
     brick.onclick = function() {
       animate({
         duration: 2000,
         timing: bounceEaseOut,
         draw: function(progress) {
           brick.style.left = progress * 500 + 'px';
         }
       });
     };
   </script>
 </body>
</html>

In case, there is an animation effect at the start, it will be demonstrated at the end. (如果在开始时有动画效果,将在结束时演示。)

In the graph above, red color demonstrates the regular bounce, and blue color- the easOut bounce. Within the regular bounce, the object bounces at the bottom then sharply jumps to the top, at the end. After easeOut, first, it jumps to the top, then bounces there. (在上图中,红色表示常规反弹,蓝色表示easOut反弹。 在常规反弹中,物体在底部反弹,然后在最后大幅跳到顶部。 缓冲后,它首先跳到顶部,然后在那里反弹。)

EaseInOut

Alos, the effect can be shown both at the start and the end of the animation. Such kind of transform is called “easeInOut”. The calculation of the timing function is shown below:

if (fractionOfTime <= 0.5) { // first half of the animation
 return time(2 * fractionOfTime) / 2;
} else { // second half of the animation
 return (2 - time(2 * (1 - fractionOfTime))) / 2;
}

Here is the wrapper code:

function makeEaseInOut(time) {
 return function (fractionOfTime) {
   if (fractionOfTime < .5)
     return time(2 * fractionOfTime) / 2;
   else
(adj.其他)
     return (2 - timing(2 * (1 - fractionOfTime))) / 2;
 }
}
bounceEaseInOut = makeEaseInOut(bounce);

The action of bounceEaseInOut is the following:

<!DOCTYPE HTML>
<html>
 <head>
   <title>Title of the Document</title>
   <script src="https://js.cx/libs/animate.js"></script>
   <style>
     #brick {
       width: 50px;
       height: 25px;
       background: blue;
       position: relative;
       cursor: pointer;
     }
     #path {
       outline: 1px solid #E8C48E;
       width: 540px;
       height: 25px;
     }
   </style>
 </head>
 <body>
   <div id="path">
     <div id="brick"></div>
   </div>
   <script>
     function makeEaseInOut(time) {
       return function(fractionOfTime) {
         if(fractionOfTime < .5) return time(2 * fractionOfTime) / 2;
         else return(2 - time(2 * (1 - fractionOfTime))) / 2;
       }
     }
     function bounce(fractionOfTime) {
       for(let a = 0, b = 1, result; 1; a += b, b /= 2) {
         if(fractionOfTime >= (7 - 4 * a) / 11) {
           return -Math.pow((11 - 6 * a - 11 * fractionOfTime) / 4, 2) + Math.pow(b, 2)
(return -Math.pow ((11 - 6 * a - 11 * fractionOfTime)/4, 2) + Math.pow (b, 2))
         }
       }
     }
     let bounceEaseInOut = makeEaseInOut(bounce);
     brick.onclick = function() {
       animate({
         duration: 2000,
         timing: bounceEaseInOut,
         draw: function(progress) {
           brick.style.left = progress * 500 + 'px';
         }
       });
     };
   </script>
 </body>
</html>

So, two graphs are joined into one by the easeInOut: the regular easeIn for the first half, and the reversed easeOut- for the second part.

So, the first part of the animation is scaled down easeIn, and the second part - easeOut. The animation begins and ends with the same effect. (因此,动画的第一部分按比例缩小了easeIn ,第二部分按比例缩小了easeOut。动画以相同的效果开始和结束。)

Summary

Summary (概要)

The JavaScript animation is a unique means that allow making complex animations. So, the animations that CSS can’t handle, can be dealt with using JavaScript. (JavaScript动画是一种独特的手段,允许制作复杂的动画。因此, CSS无法处理的动画可以使用JavaScript处理。)

As a rule, the JavaScript animations are implemented through requestAnimationFrame. This built-in method helps to set a callback function to run at the moment the browser will be preparing a repaint. Generally, it happens soon, the browser specifies the exact time. (通常, JavaScript动画通过requestAnimationFrame实现。此内置方法有助于设置在浏览器准备重新绘制时运行的回调函数。一般来说,这种情况很快就会发生,浏览器会指定确切的时间。)



请遵守《互联网环境法规》文明发言,欢迎讨论问题
扫码反馈

扫一扫,反馈当前页面

咨询反馈
扫码关注
返回顶部