type
status
date
slug
summary
tags
category
icon
password
Meta Description: 学习如何创建一个 React 自定义 Hook (
useNumberAnimation),使用 requestAnimationFrame 实现从 0 平滑增长到目标数字的动画效果。包含完整代码、用法示例、性能优化技巧,提升网页交互体验。(文章正文)
引言:告别生硬的数字跳转
在浏览数据看板、统计展示等类型的网页时,你是否注意到,很多数字并非瞬间从 0 跳变到最终值(例如 99999)?这种直接的变化显得有些生硬。取而代之的是一种平滑的数字增长动画效果,数值从起始点逐步、流畅地增加到目标值,如下图所示:
这种动画效果不仅视觉上更吸引人,也给用户一种动态、实时的感觉。那么,如何在 React 项目中实现这种效果呢?本文将带你一步步创建一个可复用的自定义 Hook (
useNumberAnimation) 来轻松实现它。核心思路:构建 useNumberAnimation Hook
我们将创建一个名为
use-number-animation.ts 的文件来封装这个逻辑。1. 确定 Hook 的输入与输出 (API 设计)
一个健壮的 Hook 需要清晰的输入参数:
from(number): 动画开始时的数字,默认为 0。
to(number): 动画结束时的目标数字。
duration(number): 动画持续的总时间(毫秒),默认为 2000ms。
- (更新) 我们将让 Hook 直接返回动画中的当前数字,而不是使用回调。
所以,Hook 的签名大致如下:
useNumberAnimation(to: number, options?: { from?: number; duration?: number }): number2. 动画引擎:requestAnimationFrame 的选择
数字动画的核心在于定时更新显示的数值。我们可能会首先想到
setInterval,但对于流畅的动画效果,requestAnimationFrame (rAF) 是更优的选择。为什么选择
requestAnimationFrame?- 性能更佳: rAF 会在浏览器下一次重绘之前执行回调函数,确保动画与浏览器的刷新率同步(通常是 60fps),避免不必要的计算和渲染。
- 更平滑: 相比
setInterval可能出现的掉帧或卡顿,rAF 提供了更平滑、自然的动画过渡。
- 资源优化: 当页面或标签页处于非活动状态时,浏览器会自动暂停 rAF 的执行,节省 CPU 资源。
3. 计算增长逻辑
动画的每一帧都需要计算出当前应该显示的数字。这基于已消耗时间和总持续时间的比例。
- 总增长量 (
range):to - from
- 动画开始时间 (
startTime): 记录动画启动的精确时间戳。
- 当前时间 (
currentTime): 在 rAF 回调中获取当前时间戳。
- 已消耗时间 (
elapsedTime):currentTime - startTime
- 进度比例 (
progress):elapsedTime / duration(限制在 0 到 1 之间)
- 当前值 (
currentValue):from + range * progress
4. 动画终止条件
动画何时停止?当
elapsedTime 大于或等于 duration 时,动画就应该结束。此时,应确保显示的数字精确地等于目标值 to。最终代码实现 (use-number-animation.ts)
结合以上思路,我们可以编写出
useNumberAnimation Hook:如何在 React 组件中使用
使用这个 Hook 非常简单:
注意事项与优化
- 性能考量:
requestAnimationFrame本身性能良好。我们在 Hook 内部通过useState更新数字。React 会进行 Diffing,通常不会有大的性能问题。如果更新频率极高且伴随复杂计算,可考虑使用useRef存储数字,仅在必要时(如动画结束或特定间隔)触发状态更新,但这会牺牲动画的实时平滑性。对于数字展示,当前实现通常足够。
- 浮点数精度: 计算
currentValue时会产生浮点数。在显示时,通常需要使用Math.round(),toFixed()或toLocaleString()进行格式化。
- 避免重复渲染问题 (原文提及): 原文提到了因频繁
setState相同值导致的 React 报错。在我们改进后的 Hook 中,由于状态由 Hook 内部管理,并且 React 自身会优化相同值的 state 更新,这个问题基本不会在 Hook 使用层面出现。如果你的组件在接收到animatedNumber后执行了非常耗时的操作,那需要对你组件的逻辑进行优化,而非 Hook 本身。 - (原文图示参考)
- (原文图示参考)
- (在我们新的 Hook 设计中通常不需要在消费端做此判断)
- 依赖项管理:
useEffect的依赖项数组 ([to, from, duration]) 很重要,确保在这些关键参数变化时,动画能正确地重新启动或更新。
最终效果
通过使用
useNumberAnimation Hook,你可以轻松地在你的 React 应用中实现平滑的数字增长动画效果,提升用户界面的动态感和吸引力。总结
本文介绍了一种使用 React 自定义 Hook 和
requestAnimationFrame 来创建数字增长动画的方法。这种方式代码简洁、可复用性强,并且性能良好。下次当你需要展示动态变化的数字时,不妨试试这个方法,给你的用户带来更流畅、愉悦的视觉体验!- 作者:90_blog
- 链接:https://blog.tri7e.com/article/react_jindu
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
