Javascript前端教学:requestAnimationFrame最简单的例子

requestAnimationFrame 是浏览器提供的用于实现平滑动画的 API。它会在浏览器下一次重绘之前调用指定的回调函数,从而保证动画与屏幕刷新率同步,避免不必要的性能消耗。

下面给出两个最简单的实用案例,帮助你快速上手。


案例一:方块左右来回移动(基础循环动画)

这个例子让一个蓝色方块在页面中从左向右移动,碰到右边界后反向,碰到左边界再折返,形成来回摆动的效果。

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>requestAnimationFrame 基础移动</title>
  <style>
    #box {
      width: 80px;
      height: 80px;
      background: #3498db;
      border-radius: 8px;
      position: absolute;
      top: 100px;
      left: 0;
    }
  </style>
</head>
<body>
  <div id="box"></div>

  <script>
    const box = document.getElementById('box');
    let x = 0;            // 当前水平位置
    let direction = 1;    // 1 向右, -1 向左
    const speed = 3;      // 每帧移动像素数

    function animate() {
      // 更新位置
      x += speed * direction;

      // 边界检测(假设视口宽度为窗口宽度)
      const maxX = window.innerWidth - box.offsetWidth;
      if (x >= maxX || x <= 0) {
        direction *= -1;  // 反向
      }

      // 应用位置
      box.style.left = x + 'px';

      // 继续下一帧
      requestAnimationFrame(animate);
    }

    // 启动动画
    requestAnimationFrame(animate);
  </script>
</body>
</html>

要点

  • 每帧直接更新 left 属性,配合 position: absolute
  • 使用 direction 变量控制运动方向,碰到边界即反转。
  • 循环调用 requestAnimationFrame(animate) 形成无限动画。

案例二:进度条 0→100% 匀速填充(基于时间戳控制)

这个例子展示如何利用 requestAnimationFrame 回调中的时间戳(DOMHighResTimeStamp)来控制动画进度,而不是依赖固定的帧率。进度条在 3 秒内从 0% 填充到 100%。

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>requestAnimationFrame 进度条</title>
  <style>
    #progress-container {
      width: 400px;
      height: 30px;
      background: #ecf0f1;
      border-radius: 15px;
      overflow: hidden;
      margin: 50px auto;
    }
    #progress-bar {
      width: 0%;
      height: 100%;
      background: #e67e22;
      border-radius: 15px;
      transition: none; /* 我们手动控制,不用CSS过渡 */
    }
    #info {
      text-align: center;
      font-family: Arial, sans-serif;
      font-size: 18px;
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <div id="progress-container">
    <div id="progress-bar"></div>
  </div>
  <div id="info">0%</div>

  <script>
    const bar = document.getElementById('progress-bar');
    const info = document.getElementById('info');
    const duration = 3000; // 持续 3 秒
    let startTime = null;   // 记录动画开始的时间戳

    function updateProgress(timestamp) {
      // 第一次调用时记录起始时间
      if (startTime === null) {
        startTime = timestamp;
      }

      // 计算已经过去的时间(毫秒)
      const elapsed = timestamp - startTime;
      // 计算进度(0~1),并限制最大为 1
      const progress = Math.min(elapsed / duration, 1);

      // 更新进度条宽度和文字
      const percent = progress * 100;
      bar.style.width = percent + '%';
      info.textContent = Math.round(percent) + '%';

      // 如果未完成,继续下一帧
      if (progress < 1) {
        requestAnimationFrame(updateProgress);
      }
    }

    // 启动动画
    requestAnimationFrame(updateProgress);
  </script>
</body>
</html>

要点

  • 回调函数的第一个参数 timestamp 是浏览器提供的精确时间戳(毫秒)。
  • 通过计算 elapsed / duration 得到 0~1 的进度值,保证动画在固定时间内完成,不受帧率波动影响。
  • 当进度达到 1 时停止循环,避免不必要的性能开销。

这两个案例覆盖了 基于帧的连续移动基于时间的可控动画 ,是 requestAnimationFrame 最常用的两种模式。你可以在此基础上扩展更复杂的动画效果。