JavaScript 动画应该通过 requestAnimationFrame 实现。该内置方法允许设置回调函数,以便在浏览器准备重绘时运行。那通常很快,但确切的时间取决于浏览器。

    callback 得到一个参数 —— 从页面加载开始经过的毫秒数。这个时间也可通过调用 performance.now() 得到。

    当页面在后台时,根本没有重绘,因此回调将不会运行:动画将被暂停并且不会消耗资源。那很棒。
    这是设置大多数动画的 helper 函数 animate:

    1. function animate({ timing, draw, duration }) {
    2. let start = performance.now();
    3. requestAnimationFrame(function animate(time) {
    4. // timeFraction 从 0 增加到 1
    5. let timeFraction = (time - start) / duration;
    6. if (timeFraction > 1) timeFraction = 1;
    7. // 计算当前动画状态
    8. let progress = timing(timeFraction);
    9. draw(progress); // 绘制
    10. if (timeFraction < 1) {
    11. requestAnimationFrame(animate);
    12. }
    13. });
    14. }

    参数:

    • duration —— 动画运行的总毫秒数。
    • timing —— 计算动画进度的函数。获取从 0 到 1 的小数时间,返回动画进度,通常也是从 0 到 1。
    • draw —— 绘制动画的函数。

    当然我们可以改进它,增加更多花里胡哨的东西,但 JavaScript 动画不是经常用到。它们用于做一些有趣和不标准的事情。因此,您大可在必要时再添加所需的功能。
    JavaScript 动画可以使用任何时序函数。与 CSS 不同,我们不仅限于 Bezier 曲线。
    draw 也是如此:我们可以将任何东西动画化,而不仅仅是 CSS 属性。

    练习:跳动的小球
    http://js.jirengu.com/gucogeceve/1/edit?html,js,output

    1. <!DOCTYPE HTML>
    2. <html>
    3. <head>
    4. <script src="https://js.cx/libs/animate.js"></script>
    5. <style>
    6. #field {
    7. height: 200px;
    8. border-bottom: 3px black groove;
    9. position: relative;
    10. }
    11. #ball {
    12. position: absolute;
    13. cursor: pointer;
    14. }
    15. </style>
    16. </head>
    17. <body>
    18. <div id="field">
    19. <img src="https://js.cx/clipart/ball.svg" width="40" height="40" id="ball">
    20. </div>
    21. <script>
    22. function makeEaseOut(timing) {
    23. return function(timeFraction) {
    24. return 1 - timing(1 - timeFraction);
    25. }
    26. }
    27. function bounce(timeFraction) {
    28. for (let a = 0, b = 1, result; 1; a += b, b /= 2) {
    29. if (timeFraction >= (7 - 4 * a) / 11) {
    30. return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)
    31. }
    32. }
    33. }
    34. function quad(timeFraction) {
    35. return Math.pow(timeFraction, 2);
    36. }
    37. ball.onclick = function() {
    38. let height = field.clientHeight - ball.clientHeight;
    39. let width = 100
    40. animate({
    41. duration: 2000,
    42. timing: makeEaseOut(bounce),
    43. draw(progress) {
    44. ball.style.top = height * progress + 'px'
    45. }
    46. });
    47. animate({
    48. duration: 2000,
    49. timing: makeEaseOut(quad),
    50. draw(progress) {
    51. ball.style.left = width * progress + 'px'
    52. }
    53. });
    54. };
    55. </script>
    56. </body>
    57. </html>