JavaScript 动画终极教程

动画的本质是在短时间内连续改变元素的视觉属性(如位置、大小、颜色、透明度等),利用人眼的视觉暂留效应,使其看起来平滑地运动起来。

javascript 动画教程
(图片来源网络,侵删)

我们将从最核心的概念开始,逐步深入到现代、高性能的动画技术。


第一部分:基础篇 - setIntervalsetTimeout

这是最传统、最直观的动画实现方式。

核心思想

通过 setIntervalsetTimeout 在一个固定的时间间隔(例如每 16ms,约等于 60fps)内,重复地更新元素的样式,从而产生动画效果。

setInterval 方法

setInterval 会每隔指定的时间执行一次函数,非常适合创建循环动画。

javascript 动画教程
(图片来源网络,侵删)

示例:一个简单的方块移动

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">JS Animation - setInterval</title>
    <style>
        #box {
            width: 50px;
            height: 50px;
            background-color: blue;
            position: absolute; /* 必须是定位元素才能改变 top/left */
            top: 0;
            left: 0;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <script>
        const box = document.getElementById('box');
        let position = 0;
        const speed = 2; // 每次移动的像素
        // 使用 setInterval 创建动画循环
        const animationInterval = setInterval(() => {
            // 更新位置
            position += speed;
            // 应用新的位置
            box.style.left = position + 'px';
            // 如果方块移出视口,停止动画
            if (position > window.innerWidth) {
                clearInterval(animationInterval); // 清除定时器
            }
        }, 16); // 大约 60fps (1000ms / 60 ≈ 16.7ms)
    </script>
</body>
</html>

优点:

  • 简单易懂,适合初学者理解动画的基本原理。

缺点:

  • 性能不佳setInterval 是独立于浏览器渲染循环的,它不管浏览器是否在重绘页面,都会执行,这可能导致掉帧,动画卡顿。
  • 不精确:JavaScript 主线程被其他任务阻塞,setInterval 的回调可能会被延迟执行,导致动画时间不准确。
  • 难以控制:暂停、恢复、反向等操作比较繁琐。

第二部分:进阶篇 - requestAnimationFrame

这是现代浏览器提供的专业动画 API,是制作网页动画的标准方式

javascript 动画教程
(图片来源网络,侵删)

核心思想

requestAnimationFrame 会告诉浏览器:“我想要在下一次重绘之前执行一个动画,请为我安排这个更新。” 它与浏览器的刷新率(通常是 60fps)同步,确保动画只在需要的时候更新,并且不会造成不必要的性能开销。

requestAnimationFrame 方法

示例:使用 requestAnimationFrame 实现方块移动

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">JS Animation - requestAnimationFrame</title>
    <style>
        #box {
            width: 50px;
            height: 50px;
            background-color: red;
            position: absolute;
            top: 50px; /* 从顶部50px开始 */
            left: 0;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <script>
        const box = document.getElementById('box');
        let position = 0;
        const speed = 2;
        let animationId; // 用于存储 requestAnimationFrame 的 ID
        // 动画循环函数
        function animate() {
            // 更新位置
            position += speed;
            box.style.left = position + 'px';
            // 如果方块还在视口内,继续请求下一帧
            if (position < window.innerWidth) {
                // 这是关键:递归调用,形成循环
                animationId = requestAnimationFrame(animate);
            } else {
                // 如果移出视口,停止动画
                cancelAnimationFrame(animationId); // 取消动画
            }
        }
        // 启动动画
        animate();
        // 如果需要暂停动画,可以调用 cancelAnimationFrame(animationId);
        // 如果需要恢复,再次调用 animate();
    </script>
</body>
</html>

优点:

  • 高性能:与浏览器渲染周期同步,自动优化,减少不必要的计算和绘制,动画更流畅。
  • 省电:当浏览器标签页处于非活动状态时,requestAnimationFrame 会自动暂停,从而节省电量。
  • 更流畅:提供更精确的动画控制,避免卡顿。
  • 易于控制:只需一个 cancelAnimationFrame 就能轻松停止动画。

缺点:

  • 对于非常简单的动画,代码量比 setInterval 稍多。

第三部分:现代动画库 - GSAP (GreenSock Animation Platform)

当动画变得复杂(如多个元素、时间轴、缓动效果、物理效果等),手动编写 requestAnimationFrame 会变得非常困难,这时,专业的动画库是更好的选择。

GSAP 是业界公认最强大、最流畅的 JavaScript 动画库。

核心思想

通过提供简洁的 API,让你用几行代码就能实现复杂的动画效果,并内置了大量的性能优化。

使用 GSAP 实现动画

你需要通过 npm 或 CDN 引入 GSAP。

CDN 引入:

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>

示例:使用 GSAP 移动方块并添加缓动效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">JS Animation - GSAP</title>
    <style>
        #box {
            width: 50px;
            height: 50px;
            background-color: green;
            position: absolute;
            top: 100px;
            left: 0;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
    <script>
        const box = document.getElementById('box');
        // gsap.to() 是最常用的方法,表示“动画到某个状态”
        // 参数1: 目标元素
        // 参数2: 动画配置对象
        gsap.to(box, {
            duration: 2, // 动画持续2秒
            x: window.innerWidth - 50, // 水平移动到窗口宽度减去方块宽度
            ease: "power2.inOut", // 缓动函数,让动画更自然
            repeat: 2, // 重复2次 (总共3次)
            yoyo: true, // 来回运动
            onComplete: () => {
                console.log("动画完成!");
            }
        });
    </script>
</body>
</html>

GSAP 的优势:

  • 极其强大:支持时间轴、复杂序列、物理效果、SVG 动画等。
  • 性能卓越:底层经过高度优化,性能甚至超过 requestAnimationFrame
  • 丰富的缓动效果:内置大量缓动函数,让动画生动自然。
  • 跨浏览器兼容性好:处理了各种浏览器的兼容性问题。

第四部分:CSS 与 JS 动画的抉择

什么时候用 CSS,什么时候用 JS?

特性 CSS 动画 JavaScript 动画
性能 极高,由浏览器直接渲染,利用 GPU 加速。 requestAnimationFrame 性能很好,但通常不如 CSS。
代码量 ,声明式语法,简洁。 ,命令式语法,需要编写逻辑。
控制力 ,难以与 JS 逻辑动态交互(如根据数据变化动画)。 极强,可以精确控制每一帧,与任何 JS 逻辑无缝集成。
复杂度 简单,适合固定的、预定义的动画。 复杂,适合动态的、复杂的、交互式的动画。
兼容性 较新属性(如 @property)可能需要前缀。 requestAnimationFrame 现代浏览器支持良好。

决策指南:

  • 优先使用 CSS 动画:当你的动画是固定的、简单的、不需要与 JS 交互时(悬停效果、页面加载时的淡入、无限循环的旋转等)。
  • 使用 JavaScript 动画
    • 当动画需要根据用户交互或数据变化而改变时(拖拽、滚动视差、根据数据变化的图表动画)。
    • 当你需要对动画进行精细的、逐帧的控制时。
    • 当动画非常复杂,涉及多个元素协同工作时(使用 GSAP 等库)。

第五部分:高级技巧与最佳实践

  1. 性能优化 - will-change 对于一些复杂的 CSS 动画,你可以提前告诉浏览器这个元素将会发生变化,浏览器会提前做好准备,提升性能。

    .animated-element {
      will-change: transform, opacity;
    }

    注意:不要滥用 will-change,它可能会占用大量内存,只在确实需要优化的元素上使用,并在动画结束后移除。

  2. 硬件加速 为了让浏览器利用 GPU 进行渲染,从而提升动画性能,可以对元素进行 3D 变换(即使你只是做 2D 动画)。

    .animated-element {
      transform: translateZ(0); /* 或者 translate3d(0, 0, 0) */
    }

    这会强制浏览器为该元素创建一个新的图层,动画将在该图层上进行,减轻主线程的压力。

  3. 使用 transformopacity 在动画中,优先使用 transform (如 translate, scale, rotate) 和 opacity 属性,因为这些属性不会触发浏览器的重排,只会触发重绘,性能开销远小于改变 width, height, top, left 等会引发重排的属性。

  4. 使用 Web Animations API 这是浏览器原生提供的、介于 CSS 和 JS 动画之间的 API,它提供了 JS 控制 CSS 动画的强大能力,性能接近 CSS。

    const box = document.getElementById('box');
    box.animate([
      { transform: 'translateX(0)' },
      { transform: 'translateX(300px)' }
    ], {
      duration: 1000,
      iterations: Infinity,
      direction: 'alternate'
    });

    这是一个非常有前景的 API,但目前浏览器支持度不如 GSAP 或 requestAnimationFrame 广泛。


总结与学习路径

  1. 理解原理:从 setInterval 开始,理解动画是通过“快速连续变化”实现的。
  2. 掌握核心:深入学习 requestAnimationFrame,这是所有 JS 动画的基石,学会如何启动、循环和停止动画。
  3. 实践项目:尝试用 requestAnimationFrame 实现一些小动画,如小球弹跳、进度条加载等。
  4. 拥抱工具:当项目变得复杂时,不要犹豫,直接使用 GSAP,它能让你事半功倍,并创造出惊艳的动画效果。
  5. 权衡选择:学会根据需求在 CSS 和 JS 动画之间做出明智的选择,并了解性能优化的技巧。

希望这份教程能帮助你顺利掌握 JavaScript 动画!祝你编码愉快!