requestAnimationFrame 动画API详解

更新日期: 2025-12-01 阅读: 47 标签: 动画

什么是requestAnimationFrame?

requestAnimationFrame(简称RAF)是浏览器提供的一个专门用来优化网页动画的api。它的核心作用是让浏览器在下次页面重绘之前执行我们指定的动画代码,这样就能保证动画和屏幕刷新保持同步,让动画效果更加流畅。


为什么选择requestAnimationFrame?

主要优势:

  • 自动匹配屏幕刷新率:通常是60帧/秒(每16.7毫秒执行一次)

  • 智能节能:当页面在后台标签页或窗口最小化时,自动暂停动画

  • 性能更好:浏览器可以优化多个动画,合并到一次重绘中

  • 时间更准:比setTimeout/setInterval控制动画时间更精确


基本使用方法

// 请求动画帧
const animationId = requestAnimationFrame(callback);

// 取消动画帧
cancelAnimationFrame(animationId);

回调函数会收到一个时间戳参数,表示当前帧的时间:

function callback(timestamp) {
  // timestamp是从页面加载开始到现在的毫秒数
  // 在这里更新动画状态
  
  // 如果需要继续动画,再次请求下一帧
  requestAnimationFrame(callback);
}


实际应用场景

1. 基础动画循环

let startTime;
let lastTime;

function animate(timestamp) {
  // 初始化开始时间
  if (!startTime) startTime = timestamp;
  if (!lastTime) lastTime = timestamp;

  // 计算时间差(用于基于时间的动画)
  const elapsed = timestamp - startTime;
  const deltaTime = timestamp - lastTime;
  lastTime = timestamp;

  // 更新动画状态(比如位置、透明度等)
  const progress = elapsed / 1000; // 转换成秒
  const position = progress * 100; // 每秒移动100像素

  // 应用动画效果
  element.style.transform = `translateX(${position}px)`;

  // 如果动画没结束,继续请求下一帧
  if (position < 500) { // 假设总共移动500像素
    requestAnimationFrame(animate);
  }
}

// 开始动画
requestAnimationFrame(animate);

2. 滚动事件优化

let ticking = false;

function updateScroll() {
  // 处理滚动事件的逻辑
  console.log('当前滚动位置:', window.scrollY);
  ticking = false;
}

window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(updateScroll);
    ticking = true;
  }
});

这种方法可以避免滚动事件触发太频繁导致的性能问题。

3. 带缓动效果的动画

function easeoutQuad(t) {
  return t * (2 - t);
}

function animateElement(element, start, end, duration) {
  const startTime = performance.now();

  function animation(currentTime) {
    const elapsed = currentTime - startTime;
    const progress = Math.min(elapsed / duration, 1);
    const easeProgress = easeOutQuad(progress);

    const currentValue = start + (end - start) * easeProgress;
    element.style.opacity = currentValue;

    if (progress < 1) {
      requestAnimationFrame(animation);
    }
  }

  requestAnimationFrame(animation);
}

// 使用例子:让元素慢慢显示出来
animateElement(myElement, 0, 1, 1000); // 1秒内从完全透明变成完全不透明

4. 游戏主循环

class GameLoop {
  constructor() {
    this.lastTime = 0;
    this.animationId = null;
  }

  start() {
    this.animationId = requestAnimationFrame(this.loop.bind(this));
  }

  stop() {
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }
  }

  loop(timestamp) {
    const deltaTime = timestamp - this.lastTime;
    this.lastTime = timestamp;

    // 更新游戏状态
    this.update(deltaTime);

    // 绘制游戏画面
    this.render();

    // 继续循环
    this.animationId = requestAnimationFrame(this.loop.bind(this));
  }

  update(deltaTime) {
    // 更新游戏逻辑
  }

  render() {
    // 绘制游戏画面
  }
}


浏览器兼容性

支持的浏览器版本:

  • Chrome 24+

  • Firefox 23+

  • Safari 6.1+

  • Edge 12+

  • IE 10+

兼容旧浏览器的解决方案:

对于不支持requestAnimationFrame的老浏览器(比如IE9),可以用下面的代码:

// requestAnimationFrame兼容处理
window.requestAnimationFrame = window.requestAnimationFrame || 
  window.webkitRequestAnimationFrame || 
  window.mozRequestAnimationFrame || 
  window.oRequestAnimationFrame || 
  function(callback) {
    return window.setTimeout(callback, 1000 / 60); // 大约60帧/秒
  };

// cancelAnimationFrame兼容处理
window.cancelAnimationFrame = window.cancelAnimationFrame || 
  window.webkitCancelAnimationFrame || 
  window.mozCancelAnimationFrame || 
  window.oCancelAnimationFrame || 
  function(id) {
    window.clearTimeout(id);
  };


使用注意事项

1. 保持回调函数简洁
不要在动画回调里做太复杂的计算,只处理必要的动画逻辑,避免卡顿。

2. 及时取消不需要的动画
用cancelAnimationFrame停止不再需要的动画,防止内存泄漏。

3. 注意时间精度
回调函数收到的时间戳比Date.now()更精确。

4. 后台标签页行为
当页面不在当前标签页时,浏览器可能会降低或暂停动画执行。

5. 避免频繁触发重排
在动画中少用offsetWidth这类会触发页面重新布局的属性。

6. 优先使用transform和opacity
这两个属性变化不会触发重排,性能更好。

7. 复杂动画考虑分层
对复杂动画元素使用will-change属性,提示浏览器优化。

8. iOS设备特殊处理
在iOS设备上,滚动时会暂停requestAnimationFrame,直到滚动停止。

9. 避免多层嵌套
不要在一个动画回调里嵌套多个新的requestAnimationFrame,可能导致动画延迟。

10. 监控性能
复杂动画可以用performance.mark()和performance.measure()来监控性能。


实用技巧

计算帧率:

let frameCount = 0;
let lastTime = performance.now();

function checkFPS(timestamp) {
  frameCount++;
  
  if (timestamp >= lastTime + 1000) {
    const fps = Math.round((frameCount * 1000) / (timestamp - lastTime));
    console.log(`当前帧率: ${fps}FPS`);
    frameCount = 0;
    lastTime = timestamp;
  }
  
  requestAnimationFrame(checkFPS);
}

requestAnimationFrame(checkFPS);

动画暂停和继续:

class Animator {
  constructor() {
    this.animationId = null;
    this.isPaused = false;
    this.pauseTime = 0;
  }
  
  start() {
    this.isPaused = false;
    this.animationId = requestAnimationFrame(this.animate.bind(this));
  }
  
  pause() {
    this.isPaused = true;
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
    }
  }
  
  animate(timestamp) {
    if (this.isPaused) return;
    
    // 动画逻辑...
    
    this.animationId = requestAnimationFrame(this.animate.bind(this));
  }
}


与setTimeout/setInterval对比

特性requestAnimationFramesetTimeout/setInterval
执行时机浏览器重绘前固定时间间隔
频率控制匹配屏幕刷新率固定,不考虑渲染
后台行为自动暂停继续执行
时间精度更高受主线程影响
动画效果更流畅可能卡顿

总结

requestAnimationFrame是实现高性能网页动画的最佳选择。它能和浏览器渲染机制很好地配合,提供更流畅的用户体验,是现代前端动画开发的重要工具

记住关键点:保持动画回调简洁、及时取消不需要的动画、优先使用transform和opacity属性。掌握这些技巧,你就能写出性能更好的网页动画。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://fly63.com/article/detial/13244

纯CSS3实现各种表情动画

CSS3非常强大,我们可以利用CSS3相关特性绘制很多平面图形,并且可以制作很多简单的动画。今天要分享的是一款用纯CSS3实现的可爱表情动画

Vue.js动画笔记_vue实现动画效果

Vue.js中的元素动画或页面跳转动画有多种实现方式比如:1、自己手动写CSS3动画来实现2、使用第三方CSS动画库如:Animate.css3、在构子函数中操作DOM4、使用第三方Js动画库如:Velocity.js。

新手引导动画的4种实现方式

尽量多的列举出新手引导动画的实现方式, 昨天稍微总结了一下, 实现了4种.源码在最后,如果想直接看结果的,可以拉到最后去看.这里假设所有的弹出层都是基于页面上原有的元素

为何 Canvas 内元素动画总是在颤抖?

在项目中我们可能对动画进行锁帧,帧率可能是 60 或者 30,如果我们想保证渲染不抖动,在匀速直线运动中,我们尽量保证我们设置的速度要是帧率的倍数,或者保证平均每帧移动的像素点是一样的

前端开发常用css动画代码

常用的CSS动画效果,在实际开发中经常需要用到移动、大小、闪烁、渐显、渐隐等动画效果,这篇文章就整理了些常见的动画效果分享给大家,一遍收藏使用。

css环形滚动_内容加载的环形滚动动画效果

创建一个没有背景的圆,然后声明透明度为0.1的黑色边框(看起来是灰色),修改左侧边框颜色。此时会有一个静态的看起来只有左边框有颜色的空心圆。然后声明一个该元素逆时针旋转360度的动画,并让该动画无限播放(infinite)即可

css transition 实现滑入滑出

transition是css最简单的动画。 通常当一个div属性变化时,我们会立即看的变化,从旧样式到新样式是一瞬间的,嗖嗖嗖!!!但是,如果我希望是慢慢的从一种状态,转变成另外一种状态,怎么办? transition可以做到。

动画函数的绘制及自定义动画函数

制作动画效果离不开动画运动函数,而我们用得最多的无疑就是Tween.js。根据不同的数学公式原理,Tween.js划分出了不同的动画类型,每种动画类型里面都包含以下的缓动类型:ease in 先慢后快、ease out 先块后慢、ease in out 先慢后快再慢

超酷的CSS3 loading预加载动画特效

这是一款超酷CSS3 loading预加载动画特效。该loading特效共有4种不同的效果,分别通过不同的CSS3 keyframes帧动画来完成。HTML结构:4种loading预加载动画的HTML结构分别如下

Vue 动画的封装

js提供的钩子动画before-enter、enter、after-enter,用这种方法写,所有的动画都写在了组件里,外部只需要调用这个fade组件就可以了,也不需要全局去写一些样式了,这种动画的封装是比较推荐的一种动画封装,因为它可以把所有动画的实现完整的封装在一个组件中

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!