持续动画效果实现

使用 JavaScript 实现持续动画的几种方式

问题

使用 JavaScript 实现一个持续的动画效果。

解答

方式一:requestAnimationFrame(推荐)

// 动画元素
const box = document.getElementById('box');
let position = 0;

function animate() {
  // 更新位置
  position += 2;
  if (position > 300) position = 0;
  
  // 应用样式
  box.style.transform = `translateX(${position}px)`;
  
  // 递归调用,形成持续动画
  requestAnimationFrame(animate);
}

// 启动动画
requestAnimationFrame(animate);

方式二:带时间控制的 requestAnimationFrame

const box = document.getElementById('box');
let startTime = null;
const duration = 2000; // 动画周期 2 秒

function animate(timestamp) {
  // 记录开始时间
  if (!startTime) startTime = timestamp;
  
  // 计算进度 (0 ~ 1)
  const elapsed = timestamp - startTime;
  const progress = (elapsed % duration) / duration;
  
  // 根据进度计算位置
  const position = progress * 300;
  box.style.transform = `translateX(${position}px)`;
  
  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

方式三:setInterval

const box = document.getElementById('box');
let position = 0;

const timer = setInterval(() => {
  position += 2;
  if (position > 300) position = 0;
  
  box.style.transform = `translateX(${position}px)`;
}, 16); // 约 60fps

// 停止动画
// clearInterval(timer);

完整示例

<!DOCTYPE html>
<html>
<head>
  <style>
    #box {
      width: 50px;
      height: 50px;
      background: #3498db;
    }
  </style>
</head>
<body>
  <div id="box"></div>
  <button id="start">开始</button>
  <button id="stop">停止</button>
  
  <script>
    const box = document.getElementById('box');
    let position = 0;
    let animationId = null;
    
    function animate() {
      position += 2;
      if (position > 300) position = 0;
      box.style.transform = `translateX(${position}px)`;
      animationId = requestAnimationFrame(animate);
    }
    
    document.getElementById('start').onclick = () => {
      if (!animationId) animate();
    };
    
    document.getElementById('stop').onclick = () => {
      cancelAnimationFrame(animationId);
      animationId = null;
    };
  </script>
</body>
</html>

关键点

  • requestAnimationFrame 会在浏览器下次重绘前执行回调,通常是 60fps
  • requestAnimationFrame 在页面不可见时会自动暂停,节省性能
  • 使用 cancelAnimationFrame 停止动画
  • 回调函数接收一个时间戳参数,可用于精确控制动画进度
  • 避免使用 setInterval,它不会与浏览器刷新率同步,可能造成掉帧