requestAnimationFrame 动画API详解

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

什么是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

相关推荐

css transition 实现滑入滑出

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

css3贝塞尔曲线(cubic-bezier)

css3 animation模块,其中animation-timing-function 和 transition-timing-function两个属性来控制动画速度分别提供了ease,liner,ease-in,ease-out

CSS动画@keyframes的用法

CSS动画允许大多数HTML元素的动画,而无需使用JavaScript或Flash!IE10+支持该属性的。其他低浏览器版本数字后跟-ms-, -webkit-,-moz-或-o-指定使用前缀的第一个版本。

css @keyframes属性 语法

@keyframes是CSS的一种规则,可以用来定义CSS动画的一个周期的行为,创建简单的动画。作用:通过 @keyframes 规则,您能够创建动画。

css3实现椭圆轨迹旋转

X轴Y轴在一个矩形内移动;设置动画延时设置Y轴延时为动画时长的一半, 运动轨迹变成菱形;设置三次贝塞尔曲线,为了看起来有立体感添加scale属性,scale动画应该是X轴和Y轴的时间总和

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

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

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

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

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

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

只用 CSS 就能做到的像素画/像素动画

这篇文章将会介绍只用 CSS 就能制作像素画·像素动画的方法。虽说纯 CSS 就能做到,但是为了更高的可维护性,也会顺便介绍使用 Sass 的制作方法。

css3 过渡和动画

在没有过渡属性的时候,当一个元素的属性值发生变化时,浏览器就会将个这个元素瞬间渲染成新属性值的样式。例如一个定位元素top:0,动态修改成top:100px,这个元素就瞬间跑到100px的位置,有时候我们为了达到某种视觉效果

点击更多...

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