我会从 “为什么用 JavaScript 动画” 开始,然后深入到 “核心原理”、“主流技术方案”、“性能优化”,最后以 “一个完整的实践案例”“学习资源” 结束。

javascript网页动画设计
(图片来源网络,侵删)

为什么使用 JavaScript 动画?

在 CSS3 动画如此强大的今天,我们为什么还需要 JavaScript?

  1. 复杂性和交互性:CSS 难以实现基于用户交互、数据变化或复杂物理模拟的动画,一个根据鼠标移动而倾斜的卡片,或一个数据可视化图表的动态变化。
  2. 精确控制:JS 可以精确控制动画的每一帧,获取和修改 DOM 元素的任何属性(位置、大小、颜色、透明度,甚至是 clip-pathfilter 等)。
  3. 与业务逻辑紧密结合:动画常常是应用状态的一部分,JS 可以轻松地在数据更新时触发动画,或者在动画结束后执行回调函数,更新应用状态。
  4. 跨浏览器一致性:虽然现代浏览器对 CSS 动画支持很好,但在处理一些复杂或旧版浏览器兼容性问题时,JS(配合库)可以提供更统一的解决方案。

动画的核心原理:帧 与 时间

无论你使用哪种技术,网页动画的本质都是 “欺骗”人眼

  • :动画是由一系列静止的图像快速连续播放而成的,每一幅图像就是一“帧”。
  • 刷新率:屏幕每秒刷新的次数,单位是赫兹,常见的有 60Hz(60次/秒)、120Hz 等。
  • 帧率:你的动画每秒播放多少帧,为了达到流畅的视觉效果,目标帧率是 60 FPS (Frames Per Second)。

如何实现 60 FPS? 浏览器大约每 16.7 毫秒(1000ms / 60 ≈ 16.7ms)会重绘一次屏幕,如果你的动画更新逻辑能在这个时间内完成,就能达到 60 FPS。


主流的 JavaScript 动画技术方案

从底层到高级,主要有以下几种方案:

javascript网页动画设计
(图片来源网络,侵删)

requestAnimationFrame (rAF) - 现代标准

这是目前 最推荐、最主流 的实现高性能动画的方式。

  • 是什么:一个浏览器提供的 API,用于在下一次浏览器重绘之前调用一个指定的函数,它会自动优化动画的时机,与浏览器的刷新率同步。

  • 优点

    • 性能最佳:当浏览器标签页处于非活动状态时,它会自动暂停动画,节省 CPU 和电量。
    • 自动同步:无需手动计算时间间隔,直接与浏览器渲染周期绑定。
    • 简单易用:API 非常简洁。
  • 基本用法

    javascript网页动画设计
    (图片来源网络,侵删)
    let element = document.getElementById('my-box');
    let position = 0;
    function animate() {
      // 更新位置
      position += 2;
      element.style.transform = `translateX(${position}px)`;
      // 请求下一帧
      if (position < 300) { // 简单的停止条件
        requestAnimationFrame(animate);
      }
    }
    // 启动动画
    requestAnimationFrame(animate);

Web Animations API (WAAPI) - 未来趋势

这是一个更高级、更强大的原生 API,旨在成为 CSS Animations 和 Transitions 的 JavaScript 等价物。

  • 是什么:直接在 DOM 元素上创建和控制动画。

  • 优点

    • 语法简洁:使用 element.animate() 方法,非常直观。
    • 功能强大:可以轻松控制时间函数、延迟、迭代次数、播放方向等。
    • 性能优越:底层同样由浏览器优化,性能接近 CSS。
    • Promise 支持:动画可以返回一个 Promise,方便使用 then()async/await 处理动画结束事件。
  • 基本用法

    const element = document.getElementById('my-box');
    // 定义关键帧
    const keyframes = [
      { transform: 'translateX(0)' },
      { transform: 'translateX(300px)' }
    ];
    // 定义动画选项
    const options = {
      duration: 2000, // 2秒
      iterations: Infinity, // 无限循环
      direction: 'alternate' // 来回交替
    };
    // 创建并播放动画
    const animation = element.animate(keyframes, options);
    // 监听动画结束
    animation.onfinish = () => console.log('Animation finished!');

CSS + JavaScript 结合 - 最灵活

这是最常见和实用的模式:用 JS 控制动画的触发和状态,用 CSS 来定义动画的具体表现和过渡效果

  • 场景:点击按钮后,给元素添加一个 CSS class,该 class 包含了 transitionanimation 属性。

  • 优点

    • 性能好:将动画的渲染工作交由浏览器高效的 CSS 引擎处理。
    • 代码分离:样式和行为分离,代码更清晰。
    • 易于维护:修改动画效果只需改 CSS,无需动 JS。
  • 基本用法

    <style>
      #my-box {
        width: 100px;
        height: 100px;
        background-color: blue;
        /* 定义过渡效果 */
        transition: transform 1s ease-in-out, background-color 0.5s;
      }
      .is-active {
        /* 定义最终状态 */
        transform: translateX(300px) rotate(360deg);
        background-color: red;
      }
    </style>
    <div id="my-box"></div>
    <button id="start-btn">Start Animation</button>
    const box = document.getElementById('my-box');
    const btn = document.getElementById('start-btn');
    btn.addEventListener('click', () => {
      // JS 只负责添加/移除 class
      box.classList.add('is-active');
      // 可以通过 JS 监听过渡结束事件
      box.addEventListener('transitionend', (event) => {
        if (event.propertyName === 'transform') {
          console.log('Transform transition finished!');
        }
      });
    });

第三方动画库 - 功能强大,开箱即用

对于复杂的动画需求,使用成熟的库是最高效的选择。

  • GSAP (GreenSock Animation Platform)
    • 业界标杆:性能之王,功能极其强大,拥有复杂的动画时间线、缓动函数、滚动动画等。
    • 特点:轻量级、高性能、兼容性好。
  • anime.js
    • 轻量且优雅:API 设计非常现代和流畅,支持 CSS 属性、SVG、JS 对象等多种属性。
    • 特点:学习曲线平缓,功能强大。

性能优化黄金法则

糟糕的动画会导致页面卡顿、掉帧,严重影响用户体验,遵循以下原则:

  1. 使用 transformopacity

    • 这两个属性由 合成器线程 处理,不会触发整个页面的重排,性能开销极小。
    • 避免频繁修改 width, height, margin, top, left 等会触发 重排 的属性。
  2. 使用 will-change 提前告知浏览器

    • will-change 是一个 CSS 属性,用于提前告诉浏览器某个元素将要发生变化,让浏览器提前做好准备(为该元素创建新的图层)。
    • 用法.animated-box { will-change: transform, opacity; }
    • 警告:不要滥用!只在即将发生复杂动画的元素上使用,并在动画结束后移除该属性,否则会占用大量内存。
  3. 避免在每一帧中进行昂贵的操作

    • 不要在 requestAnimationFrame 回调中进行 DOM 查询、复杂的计算或布局抖动。
    • 将这些操作放在动画循环之外,或者使用节流/防抖技术。
  4. 硬件加速

    • 对于移动元素,可以强制浏览器为其创建一个独立的图层,利用 GPU 加速渲染。
    • 最简单的方法是:transform: translateZ(0);transform: translate3d(0, 0, 0);,这通常会触发硬件加速。

实践案例:创建一个平滑的跟随鼠标的卡片

这个案例结合了 requestAnimationFrametransform,是 JS 动画的经典应用。

目标:一个卡片会平滑地跟随鼠标移动,但有一个延迟效果,看起来更自然。

HTML:

<div class="container">
  <div id="card" class="card">
    <h2>Follow Me!</h2>
    <p>Move your mouse around.</p>
  </div>
</div>

CSS:

body {
  margin: 0;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  background: #f0f0f0;
  overflow: hidden; /* 防止滚动条 */
}
.container {
  position: relative;
  width: 100%;
  height: 100%;
}
.card {
  width: 200px;
  height: 280px;
  background: linear-gradient(135deg, #6e8efb, #a777e3);
  border-radius: 15px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: white;
  text-align: center;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
  /* 关键:使用 transform 进行动画,性能最佳 */
  transform-style: preserve-3d; /* 如果需要 3D 效果 */
  transition: box-shadow 0.3s ease; /* 阴影变化用过渡,更柔和 */
}
.card:hover {
  box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
}

JavaScript:

const card = document.getElementById('card');
// 鼠标在页面上的位置
let mouseX = 0;
let mouseY = 0;
// 卡片的当前位置
let cardX = 0;
let cardY = 0;
// 跟随的平滑系数,值越小,跟随越延迟
const speed = 0.1;
// 监听鼠标移动
document.addEventListener('mousemove', (event) => {
  // 更新目标位置(鼠标位置)
  mouseX = event.clientX;
  mouseY = event.clientY;
});
// 使用 requestAnimationFrame 创建动画循环
function animate() {
  // 计算卡片当前位置与目标位置的差值,并乘以一个系数
  // 这会产生一个缓动效果,而不是瞬间跳到目标位置
  cardX += (mouseX - cardX) * speed;
  cardY += (mouseY - cardY) * speed;
  // 使用 transform: translate 来移动卡片
  // 也可以加上 rotateZ 增加一些趣味性
  const rotateX = (mouseY - window.innerHeight / 2) / 25;
  const rotateY = (window.innerWidth / 2 - mouseX) / 25;
  card.style.transform = `
    translate3d(${cardX - window.innerWidth / 2}px, ${cardY - window.innerHeight / 2}px, 0)
    rotateX(${rotateX}deg)
    rotateY(${rotateY}deg)
  `;
  // 请求下一帧
  requestAnimationFrame(animate);
}
// 启动动画
animate();

学习资源总结

技术 优点 缺点 适用场景
requestAnimationFrame 性能最优,与浏览器同步 需要手动实现所有动画逻辑 自定义、复杂的逐帧动画
Web Animations API 语法强大,Promise 支持 兼容性稍差(IE 不支持) 需要精细控制 CSS 动画的场景
CSS + JS 性能好,代码分离 动画逻辑较简单时略显繁琐 大多数交互式UI动画(hover, click)
第三方库 功能强大,开箱即用 增加项目体积,学习成本 复杂的时间线动画、物理模拟、滚动动画

对于初学者,建议从 CSS + JS 结合开始,然后深入理解 requestAnimationFrame 的原理,当你需要处理更复杂的动画时,再考虑学习 GSAP 或 anime.js 这样的库。