如何在 React 中使用 requestAnimationFrame

发布日期:2026-07-15 11:21:04   来源 : 杭州电子商务研究院    浏览量 :3
杭州电子商务研究院 发布日期:2026-07-15 11:21:04  
3

介绍

动画是开发互动和引人入胜的网站的关键。可以使用setIntervalsetTimeout函数创建简单的动画,以在定义的间隔后更改元素的属性。一些常见的动画(如淡入/淡出、滑入/滑出或折叠/展开)通常用于在页面上显示更改,以向用户提供有意义的过渡效果。

元素动画涉及重排(高度、宽度、字体等的变化)和重绘(元素颜色、背景等的变化),这些动作是由于文档对象模型 (DOM) 的变化而发生的,目的是呈现或更新页面。由于多次调用重排/重绘过程,动画错误或缓慢可能会导致屏幕效果迟缓。

动画可以使用 CSS 创建,但它不适用于需要来自各种来源(例如用户或函数)输入的非线性动画。本指南介绍了如何使用 requestAnimationFrame方法在 React 中创建最佳动画。代码库可在github上找到。

React 中的重新加载过程

渲染过程包括计算 DOM 中的布局和样式更改以更新页面。DOM 以节点的形式表示 HTML 文档,每个节点还可以包含信息或样式(来自 CSS)节点。更改 DOM 既昂贵又耗时,因此 React 使用虚拟 DOM 来跟踪更改,然后仅使用差异算法将新更改应用于实际 DOM 中。

一系列帧组成了动画,每一帧都在屏幕上逐帧渲染。一般来说,动画应该能够每秒渲染 60 帧才能获得流畅的体验,但浏览器或系统可能并不总是能够处理 60FPS。假设用户同时打开了 20-30 个链接。

requestAnimationFrame 的用法

可以使用setTimeoutsetInterval创建一个简单的动画。setTimeout在提供的延迟后执行,需要递归调用,而setInterval在定义的间隔后重复执行代码:

      // move element down by 3px, 60 times in a second
import React, { Component } from 'react';
import { render } from 'react-dom';
import './App.css';

export default class App extends Component {
  constructor() {
    super();
    this.state = {
      name: 'Football Animation'
    };
  }

// change the top position by 3 pixels of an element that has a “circle” class value.
  moveBall = () => {
    let start = Date.now();
    let football = document.querySelector(".circle")

    let timer = setInterval(function () {
      let interval = Date.now() - start;

      football.style.top = interval / 3 + 'px'; // move element down by 3px

      if (interval > 1000) clearInterval(timer); // stop animation

    }, 1000 / 60);
  }


  render() {
    return (
      <div className="containter">
        <img className="circle" onClick={this.moveBall} />
      </div>
    );
  }
}
    
      .App {
    width: 100vw;
    height: 100vh;
}

.circle {
    left: 0;
    right: 0;
    margin: 0 auto;
    position: fixed;
    width: 75px;
    height: 75px;
    background-position: center;
    background-image: url("https://static.ltd.com/uploadfilev2/remote/0/0/760/2024-09/17262832299189.png");
    background-color: #ffffff;
    border-radius: 50px;
    background-repeat: no-repeat;
}
    

上面的代码片段将把具有圆形类值的元素向下移动,这可以正常工作,但也有一些缺点:

  • 由于系统缓慢或繁忙,无法保证在给定的延迟内调用setInterval 。
  • 使用精确的间隔还会导致不必要的重排或重绘周期。例如,每 16.66 毫秒更改一次页面将强制系统以 60 FPS 的速度处理,但浏览器或系统可能由于繁忙状态而无法执行此操作。此外,当用户位于其他选项卡上时,在页面上消耗资源是毫无意义的。

为了优化系统和浏览器资源,建议使用requestAnimationFrame,它会请求浏览器在下一个重绘周期中执行代码。这允许系统优化资源和帧速率,以减少不必要的重排/重绘调用。

使用 requestAnimationFrame 实现动画

requestAnimationFrame方法仅在浏览器准备好执行绘制操作时才调用输入动画函数。前面的moveBall函数示例只需将setInterval替换为requestAnimationFrame即可实现

      moveBall = () => {
    let start = Date.now();
    let football = document.querySelector(".circle")
    // timestamp: time elapsed in milliseconds since the web page was loaded
    let timer = requestAnimationFrame(function animateBall(timestamp) {
        let interval = Date.now() - start;

        football.style.top = interval / 3 + 'px'; // move element down

        if (interval < 1000) requestAnimationFrame(animateBall); // queue request for next frame

    });
}
    

requestAnimationFrame方法每个请求仅执行一次回调函数,需要使用requestAnimationFrame(animateBall)再次调用该回调函数来执行下一个动画帧转换。requestAnimationFrame方法返回一个整数 ID,可以使用 cancelAnimationFrame (id)方法取消排队的请求。动画回调函数还可以接受时间戳值,即自网页加载以来经过的时间(以毫秒为单位)。它提供执行回调的准确时间戳。

实现弹跳动画

让我们实现弹跳动画,使球动画更有趣和自然。要实现弹跳,需要两种方法,计算 y 轴的弹跳因子:

      bounce = (timeFraction) => {
    for (let a = 0, b = 1; 1; a += b, b /= 2) {
        if (timeFraction >= (7 - 4 * a) / 11) { // 4 and 7 coefficient are used to control bounce and smooth y axis fall
            return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) // Math.pow(b, 2) to keep the same x axis for bounce
                // -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) adjust the y axis up and down
        }
    }
}```

and the `easeOut` function that takes an animation methods and returns a wrapper method to convert the bounce y-axis factor downwards:
```JS

    easeOut = (timing) => {
        return (timeFraction) => {
            return 1 - timing(1 - timeFraction);
        }
    }
    

现在反弹函数将如下所示:

      bounceBall = () => {
        let bounceEaseOut = this.easeOut(this.bounce);
        let start = Date.now();
        let football = document.querySelector(".circle")
        let id = requestAnimationFrame(function animate(time) {
            let interval = (Date.now() - start) / 2000;
            if (interval > 1) interval = 1;

            football.style.top = bounceEaseOut(interval) * 300 + 'px' // adjust the y axis
                // football.style.left = interval * 200 + 'px' // adjust the x axis

            if (interval < 1) {
                requestAnimationFrame(animate);
            }

        })
    }
    

结论

  • 所有现代浏览器都支持 requestAnimationFrame并且支持可以追溯到 IE10。
  • 对于 GPU 密集型任务,请尽可能使用转换方法以获得更好的性能。
  • requestAnimationFrame还允许浏览器在页面不活动或系统性能不佳时阻止动画以节省电池。
  • 建议使用变换属性而不是高度和宽度属性改变,以避免启动整个页面的重新绘制。

优化的代码库可在我的bounce-react-animation存储库中找到。本指南介绍了使用requestAnimationFrame在 React 中开始使用动画的必要细节。祝您编码愉快!

以上内容来自杭州电子商务研究院推送
关注
关于我们
热门推荐
合作伙伴
免责声明:本站部分资讯来源于网络,如有侵权请及时联系客服,我们将尽快处理
Copyright © 2025-2027 ToB产业网址导航 公安备案 浙公网安备33010602013138号 浙ICP备16025413号-9
支持 反馈 关注 数据