如何在 React 中使用 requestAnimationFrame
介绍
动画是开发互动和引人入胜的网站的关键。可以使用setInterval或setTimeout函数创建简单的动画,以在定义的间隔后更改元素的属性。一些常见的动画(如淡入/淡出、滑入/滑出或折叠/展开)通常用于在页面上显示更改,以向用户提供有意义的过渡效果。
元素动画涉及重排(高度、宽度、字体等的变化)和重绘(元素颜色、背景等的变化),这些动作是由于文档对象模型 (DOM) 的变化而发生的,目的是呈现或更新页面。由于多次调用重排/重绘过程,动画错误或缓慢可能会导致屏幕效果迟缓。
动画可以使用 CSS 创建,但它不适用于需要来自各种来源(例如用户或函数)输入的非线性动画。本指南介绍了如何使用 requestAnimationFrame方法在 React 中创建最佳动画。代码库可在github上找到。
React 中的重新加载过程
渲染过程包括计算 DOM 中的布局和样式更改以更新页面。DOM 以节点的形式表示 HTML 文档,每个节点还可以包含信息或样式(来自 CSS)节点。更改 DOM 既昂贵又耗时,因此 React 使用虚拟 DOM 来跟踪更改,然后仅使用差异算法将新更改应用于实际 DOM 中。
一系列帧组成了动画,每一帧都在屏幕上逐帧渲染。一般来说,动画应该能够每秒渲染 60 帧才能获得流畅的体验,但浏览器或系统可能并不总是能够处理 60FPS。假设用户同时打开了 20-30 个链接。
requestAnimationFrame 的用法
可以使用setTimeout和setInterval创建一个简单的动画。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 中开始使用动画的必要细节。祝您编码愉快!
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~