WHAT - 通过 react-use 源码学习 React(Animations 篇)
目录
- 一、官方介绍
- 1. Sensors
- 2. UI
- 3. Animations
- 4. Side-Effects
- 5. Lifecycles
- 6. State
- 7. Miscellaneous
- 二、源码学习
- Animations - useRaf
- 业务场景
- 2. 游戏开发
- 3. 数据可视化
- 4. UI 组件
- 5. 时间驱动的 UI 效果
- 实现示例
一、官方介绍
Github 地址
react-use 是一个流行的 React 自定义 Hook 库,提供了一组常用的 Hook,以帮助开发者在 React 应用程序中更方便地处理常见的任务和功能。
官方将 react-use
的 Hook 分成了以下几个主要类别,以便更好地组织和查找常用的功能。每个类别涵盖了不同类型的 Hook,满足各种开发需求。以下是这些类别的详细说明:
1. Sensors
- 功能: 主要涉及与浏览器或用户交互相关的传感器功能。
- 示例:
useMouse
: 获取鼠标位置。useWindowSize
: 获取窗口尺寸。useBattery
: 监控电池状态。
2. UI
- 功能: 涉及用户界面相关的功能,如处理样式、显示和隐藏元素等。
- 示例:
useClickAway
: 监听点击事件以检测用户点击是否发生在组件外部。useMeasure
: 测量元素的大小和位置。useDarkMode
: 管理和检测暗模式状态。
3. Animations
- 功能: 处理动画和过渡效果。
- 示例:
useSpring
: 使用react-spring
处理动画效果。useTransition
: 使用react-spring
处理过渡动画。
4. Side-Effects
- 功能: 处理副作用相关的 Hook,包括数据获取、异步操作等。
- 示例:
useAsync
: 处理异步操作,如数据获取,并提供状态和结果。useFetch
: 简化数据获取操作。useAxios
: 使用 Axios 进行数据请求的 Hook。
5. Lifecycles
- 功能: 处理组件生命周期相关的 Hook。
- 示例:
useMount
: 在组件挂载时执行的 Hook。useUnmount
: 在组件卸载时执行的 Hook。useUpdate
: 在组件更新时执行的 Hook。
6. State
- 功能: 管理组件状态和相关逻辑。
- 示例:
useState
: 提供基本状态管理功能。useReducer
: 替代useState
实现更复杂的状态逻辑。useForm
: 管理表单状态和验证。useInput
: 管理输入字段的状态。
7. Miscellaneous
- 功能: 各种其他实用功能的 Hook,涵盖一些不容易归类到其他类别的功能。
这种分类方法使得 react-use
的 Hook 更加有组织和易于查找,帮助开发者快速找到需要的功能并有效地集成到他们的应用程序中。
二、源码学习
Animations - useRaf
re-renders component on each requestAnimationFrame.
使用
import {useRaf} from 'react-use';
const Demo = () => {
const elapsed = useRaf(5000, 1000);
return (
<div>
Elapsed: {elapsed}
</div>
);
};
源码
import { useState } from 'react';
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
const useRaf = (ms: number = 1e12, delay: number = 0): number => {
const [elapsed, set] = useState<number>(0);
useIsomorphicLayoutEffect(() => {
let raf;
let timerStop;
let start;
const onFrame = () => {
const time = Math.min(1, (Date.now() - start) / ms);
set(time);
loop();
};
const loop = () => {
raf = requestAnimationFrame(onFrame);
};
const onStart = () => {
timerStop = setTimeout(() => {
cancelAnimationFrame(raf);
set(1);
}, ms);
start = Date.now();
loop();
};
const timerDelay = setTimeout(onStart, delay);
return () => {
clearTimeout(timerStop);
clearTimeout(timerDelay);
cancelAnimationFrame(raf);
};
}, [ms, delay]);
return elapsed;
};
export default useRaf;
解释
useRaf
这个 hook 主要用于在 React 组件中实现基于 requestAnimationFrame
的计时器功能。这个功能可以用于创建动画效果或在特定的时间间隔内执行操作。以下是对这个 API 的详细解析:
useRaf
Hook 的参数
-
ms: number = 1e12
:- 这个参数定义了计时器的持续时间,单位为毫秒。默认为
1e12
(即 10^12 毫秒,约 31.7 年),通常你会根据需求设置为一个更合理的值。
- 这个参数定义了计时器的持续时间,单位为毫秒。默认为
-
delay: number = 0
:- 这个参数定义了在计时器开始之前的延迟,单位为毫秒。默认为
0
,意味着计时器会立即开始。
- 这个参数定义了在计时器开始之前的延迟,单位为毫秒。默认为
useIsomorphicLayoutEffect
函数
import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect';
useIsomorphicLayoutEffect
是一个自定义 hook,它的作用类似于useLayoutEffect
,但可以在服务端渲染和客户端渲染中都有效。通常用于需要在 DOM 更新之前执行副作用的情况。这个 hook 需要在你的项目中被实现或引入。
useLayoutEffect is a version of useEffect that fires before the browser repaints the screen.
useRaf
的内部逻辑
-
const [elapsed, set] = useState<number>(0);
:elapsed
状态用于存储从计时器开始到当前的时间进度。set
是更新这个状态的函数。
-
useIsomorphicLayoutEffect
:-
这个 hook 用于在组件渲染后执行副作用,并且在服务端渲染时也能正常工作。
-
内部副作用函数:
-
变量:
raf
: 用于存储requestAnimationFrame
返回的 ID。timerStop
: 用于存储setTimeout
返回的 ID,用于停止计时器。start
: 记录计时器开始的时间。
-
onFrame
:- 这是在每一帧时调用的函数。它计算经过的时间并更新状态
elapsed
,然后请求下一帧的动画。
- 这是在每一帧时调用的函数。它计算经过的时间并更新状态
-
loop
:- 使用
requestAnimationFrame
来持续调用onFrame
。
- 使用
-
onStart
:- 设定计时器的开始函数。
- 使用
setTimeout
来在设定的时间ms
后停止计时器,并更新状态为1
。 - 记录计时器的开始时间,并启动动画循环。
-
timerDelay
:- 使用
setTimeout
来在设定的延迟时间delay
后调用onStart
。
- 使用
-
清理函数:
- 在副作用清理阶段,清除所有
setTimeout
和requestAnimationFrame
,以避免内存泄漏和不必要的更新。
- 在副作用清理阶段,清除所有
-
-
- 返回值
return elapsed;
:- 返回从计时器开始到当前的时间进度,取值范围是 0 到 1,表示时间的百分比。
useRaf
hook 提供了一种基于 requestAnimationFrame
的计时器功能,适用于需要精确动画时间的场景。它允许你在一定的延迟后开始计时,并在设定的时间后停止计时。这个 hook 管理了动画帧的请求和取消,以及定时器的设置和清理,确保在组件卸载时不会产生内存泄漏或不必要的更新。
业务场景
useRaf
hook 基于 requestAnimationFrame
实现了一个计时器功能,它可以在 React 组件中管理时间进度。这个 hook 主要用于需要高精度动画时间的场景。以下是一些实际的业务适用场景:
1. 动画和过渡效果
-
动画进度管理:
- 用于创建基于时间的动画效果,比如渐变、缩放或位移动画。
useRaf
可以提供从 0 到 1 的进度值,帮助你精确控制动画状态。
- 用于创建基于时间的动画效果,比如渐变、缩放或位移动画。
-
复杂动画:
- 当你需要对复杂的动画进行精细控制时,
useRaf
可以帮助你在每一帧上计算和更新动画状态。例如,一个复杂的动画可能包括多个阶段或缓动效果。
- 当你需要对复杂的动画进行精细控制时,
2. 游戏开发
-
游戏循环:
- 在游戏开发中,你需要高频率的帧更新来保证流畅的游戏体验。
useRaf
可以用作游戏的主要循环,处理游戏逻辑、物体移动和渲染。
- 在游戏开发中,你需要高频率的帧更新来保证流畅的游戏体验。
-
动画帧控制:
- 处理游戏角色或物体的动画状态,确保动画与屏幕刷新同步。
3. 数据可视化
-
实时数据更新:
- 用于实时数据可视化,比如图表和图形的动态更新。
useRaf
可以帮助你在每一帧更新数据并重新渲染图表,确保平滑的视觉效果。
- 用于实时数据可视化,比如图表和图形的动态更新。
-
动态绘图:
- 在需要动态绘制图形或更新可视化组件时,
useRaf
可以确保每一帧的更新与浏览器的重绘周期一致,从而实现更流畅的用户体验。
- 在需要动态绘制图形或更新可视化组件时,
4. UI 组件
-
滚动效果:
- 创建平滑的滚动效果,比如滚动条动画或平滑滚动的内容区域。
useRaf
可以在每一帧更新滚动位置或内容。
- 创建平滑的滚动效果,比如滚动条动画或平滑滚动的内容区域。
-
拖放操作:
- 在拖放操作中,实时更新拖动元素的位置,确保拖动效果与屏幕刷新同步。
5. 时间驱动的 UI 效果
-
延迟显示:
- 在用户交互后延迟显示某些 UI 元素(例如,工具提示或菜单)。
useRaf
可以帮助你精确控制显示延迟和过渡效果。
- 在用户交互后延迟显示某些 UI 元素(例如,工具提示或菜单)。
-
定时器和计时器:
- 实现定时器或倒计时功能,确保时间进度的更新与屏幕刷新一致。
实现示例
假设你想实现一个简单的渐变动画,你可以使用 useRaf
来管理动画进度:
import React from 'react';
import useRaf from './useRaf';
const GradientAnimation = () => {
const progress = useRaf(2000, 500); // 2秒动画,500毫秒延迟
const backgroundColor = `rgba(255, 0, 0, ${progress})`; // 渐变效果
return (
<div style={{ width: '100vw', height: '100vh', backgroundColor }}>
<h1>Gradient Animation</h1>
</div>
);
};
export default GradientAnimation;
在这个例子中,useRaf
提供了一个从 0 到 1 的进度值,用于控制背景颜色的渐变效果。动画将在 500 毫秒后开始,并在 2 秒内完成。