Pulse · PulseDot · usePulse
通用脉冲原语:shared/pulse 内部统一完成参数归一化与平台驱动;Pulse、PulseDot、Skeleton 分别提供自己的默认值和诊断入口,公共 usePulse 接口保持不变。native 使用 react-native-reanimated@4 worklet,Web 使用 CSS transition + timer。三个 API:
usePulse(options)— 钩子,返回可直接拼到Animated.View上的 style<Pulse>{children}</Pulse>— 把 children 包进一个 opacity 循环动画<PulseDot />— 一个固定的圆点(默认 6×6 主橙),用于"思考中"等指示器
代码演示
下方渲染的是 PulseDot.tsx 与 Pulse.tsx 的 Web 路径:公共 hook 仍执行同一套归一化,平台解析选择 usePulseDriver.web.ts,由 CSS transition + timer 驱动;参数和 reduced-motion 语义与 native 一致。
用法
usePulse hook
import Animated from 'react-native-reanimated';
import { usePulse } from '@unif/react-native-design';
export function MyShimmerLine({ width }) {
const animatedStyle = usePulse({ from: 0.6, to: 1, duration: 700 });
return (
<Animated.View
style={[
{ width, height: 11, borderRadius: 3, backgroundColor: '#EDEDED' },
animatedStyle,
]}
/>
);
}
<Pulse> 包装组件
import { Pulse, Icon } from '@unif/react-native-design';
<Pulse from={0.4} duration={500}>
<Icon name="spark" size={14} color="#EB6E00" />
</Pulse>;
<PulseDot>
import { PulseDot } from '@unif/react-native-design';
<PulseDot /> // 默认 6×6 主橙
<PulseDot size={10} color="#3775F6" /> // 自定义
<PulseDot delay={200} /> // 错峰开始(多个排成一行做"打字中")
API
usePulse(options?)
| Option | 类型 | 默认值 | 合法域 | 说明 |
|---|---|---|---|---|
from | number | 0.6 | [0, 1] | 透明度起点(不必小于 to) |
to | number | 1 | [0, 1] | 透明度终点 |
duration | number | 700 | [1, 2³¹) | 半周期时长(ms),完整一圈 = 2 × duration |
delay | number | 0 | [0, 2³¹) | 首次开始之前的延迟(ms) |
返回可直接传给 Animated.View 的 style。
参数校验规则
四个参数都由唯一一层归一化处理(native 与 web 共用),规则是非法字段回退到所属组件默认值,不做 clamp、不做取整:
- 超出合法域、
NaN、Infinity或非number→ 整个字段回退到默认值,并在__DEV__下打一条 warn 说明收到了什么、回退成了什么。 - 合法值原样保留 ——
duration: 700.5不会被取整,duration: 0也不会被悄悄夹成1(那样调用方永远发现不了自己传错了)。 duration/delay上界是开区间2³¹:setTimeout/setInterval内部用 int32 存时长,>= 2³¹会溢出成0变成「每帧触发」。
反向脉冲与静态
from > to是合法的反向脉冲,两个值原样保留。from === to视为静态:不启动任何 timer / 动画,直接停在to。- 系统开启减弱动态效果时同样不启动动画,静止在
to(完全显示)。native 读系统设置、web 读matchMedia,详见动效 → Reduced motion。
native 走 Reanimated 4 worklet(UI 线程);web 走 CSS transition + setInterval 两档翻转(零 rAF JS 帧),不是 worklet。两端的参数语义、静态判定和 reduced-motion 行为完全一致 —— 差异只在动画驱动层(usePulseDriver.ts / usePulseDriver.web.ts)。
<Pulse>
接受 usePulse 全部 options(from / to / duration / delay)+ children + 可选 testID。把 children 渲在一个 <Animated.View> 里,opacity 走脉冲循环。
<PulseDot>
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
size | number? | 6 | 圆点直径(px) |
color | string? | c.primary(运行期 hook 取) | 填充色 |
from / to / duration / delay | number? | from=0.5 / to=1 / duration=700 / delay=0 | 与 usePulse 同一套校验规则(注意 PulseDot 的 from 默认 0.5,usePulse / <Pulse> 默认 0.6) |
testID | string? | — | E2E / 测试定位 |
无障碍(a11y)
来源:src/components/ui/Pulse/PulseDot.tsx、Pulse.tsx、usePulse.ts。
纯视觉脉冲原语,三个 API 均无交互、无 accessibilityRole:
<PulseDot>源码在其<Animated.View>上显式设accessibilityElementsHidden+importantForAccessibility="no-hide-descendants",对 SR 完全隐藏(圆点是装饰性指示器,无朗读价值)。<Pulse>是透明包装层(仅给 children 套一个 opacity 循环的<Animated.View>),自身未设置任何 a11y prop,a11y 语义完全由其children承载——把语义放在被包裹的内容上(如有意义的图标加accessibilityLabel)。usePulse只返回动画style,不涉及 a11y。
组合使用
Skeleton 与 Pulse 复用基础动效。应用也可将公开的 Pulse/PulseDot 组合到自己的内容中;状态语义由外层文字与无障碍属性表达。
使用注意
- 需要脉冲时使用
usePulse,复用参数校验、系统动效偏好和资源释放,避免在消费方重复实现。 - ❌ 不要在
useAnimatedStyle里引用 React state(worklet 闭包只能读 SharedValue)。 - options 对象可以内联:归一化按
from/to/duration/delay的实际值缓存,不因对象引用变化重启动效。 - ❌ 不要指望库会替你把非法值夹到合法域 —— 非法字段回退到默认值并 dev warn,不做 clamp。
- ❌ 不要假设 web 端跑的是 worklet —— web 是 CSS transition +
setInterval。