react-native-reanimated 节点详解:clockRunning 时钟状态检测的用法与原理
【免费下载链接】react-native-reanimatedReact Native's Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated
导读
clockRunning是 react-native-reanimated 旧版(1.x)节点 API 中用于检测 Clock 节点是否处于运行状态的核心判断节点:给定一个Clock节点,若该时钟当前正在逐帧更新(即已被startClock启动),它返回1,否则返回0。它通常与startClock、stopClock、cond配合,构成“动画是否已启动 / 是否需要重新初始化”的分支逻辑,是编写声明式时钟驱动动画(如timing、decay、spring)时的常用门控节点。读完本文,你将掌握clockRunning的调用方式、返回值语义、与启动/停止节点的协作关系,以及它在真实动画流程中的完整实战用法。
clockRunning节点是什么
clockRunning是 react-native-reanimated 1.x 文档中列出的节点之一,位于 nodes/clockRunning.md,其函数签名如下:
clockRunning(clockNode);- 参数:
clockNode,即一个Clock节点(Animated.Clock)。 - 返回值:
1(真)或0(假)——如果该时钟已通过startClock启动、正在每一帧更新,返回1;否则返回0。
从语义上看,它等价于对时钟状态的一次“布尔查询”,因此最常见的用途是作为cond的条件表达式,用来分流“动画已在进行中”与“动画尚未开始 / 需要重新初始化”两种代码路径。
与其他时钟操作节点的关系
要理解clockRunning,必须先理解它所属的“时钟操作节点”三件套,三者配套使用才能完成完整的时钟生命周期控制:
| 节点 | 作用 | 返回值 |
|---|---|---|
startClock | 让传入的 Clock 节点开始每一帧更新自身值 | 0 |
clockRunning | 查询 Clock 节点当前是否在逐帧更新 | 1(运行中)/0(未运行) |
stopClock | 停止依赖该时钟的节点被求值 | 0(未在文档中显式说明,配合cond使用) |
其中startClock的文档明确说明:一旦求值,它会使传入的 Clock 节点开始逐帧更新自身值,并返回0。而stopClock的文档给出了一条非常容易踩坑的NOTE:
即使对某个时钟调用了
stopClock,该时钟仍会继续更新自身的值;stopClock只是阻止依赖该时钟的其他节点在时钟更新时被求值。若要保存时钟停止时的时刻,请使用Value节点。
也就是说,clockRunning反映的“运行”状态与“时钟还在走”并不完全等价——时钟本身的时间戳始终在推进,stopClock切断的是从时钟出发的下游求值链路,clockRunning检测的正是这条链路是否接通。
Clock 节点的底层语义
为什么需要一个专门的“时钟状态查询”?这要从 1.x 时代 Clock 节点的设计说起。clock.md 中说明了设计动机:
- 原生
AnimatedAPI 把“动画对象”当作一等公民,动画对象功能多,需要在 JS 与原生之间维护大量桥接方法; - react-native-reanimated 用 Clock 节点提供更底层的抽象,并且 Clock 节点行为与普通动画值(
Animated.Value)非常相似,因此实现复杂度显著降低。
Animated.Clock是Animated.Value的一种特殊类型:它在每一帧被更新为当前帧的时间戳(毫秒)。当你把 Clock 节点作为输入时,它返回的值就是当前帧的时间戳(毫秒)。通过startClock/stopClock/clockRunning这三个特殊方法,可以启动、停止时钟,并检测时钟是否已启动。由于Animated.Clock只是扩展了Animated.Value,你可以在任何允许传入动画节点的操作(运算)位置使用它。
这一特性决定了clockRunning与普通值节点的区别:它不是读取某个数据值,而是读取时钟节点在节点图中“是否活跃”这一运行态信息。
实战:用clockRunning编写可复用时钟动画
clockRunning最典型的实战场景来自 declarative.md(声明式动画 API)中的runTiming示例。这段代码把“动画是否已启动”作为判断条件,实现了一个可重复调用、目标值可中途更新的 timing 动画:
import Animated, { Easing } from 'react-native-reanimated'; const { Clock, Value, set, cond, startClock, clockRunning, timing, debug, stopClock, block, } = Animated; function runTiming(clock, value, dest) { const state = { finished: new Value(0), position: new Value(0), time: new Value(0), frameTime: new Value(0), }; const config = { duration: 5000, toValue: new Value(0), easing: Easing.inOut(Easing.ease), }; return block([ cond( clockRunning(clock), [ // 时钟已在运行:只更新目标值,以便支持中途改变 dest set(config.toValue, dest), ], [ // 时钟未运行:重置全部动画参数并启动时钟 set(state.finished, 0), set(state.time, 0), set(state.position, value), set(state.frameTime, 0), set(config.toValue, dest), startClock(clock), ] ), // 执行 timing 单步,更新 position timing(clock, state, config), // 动画结束则停止时钟 cond(state.finished, debug('stop clock', stopClock(clock))), // 整个 block 的返回值为更新后的 position state.position, ]); }对照 animations/timing.md 中timing节点的参数说明,可以完整解读这段代码的协作关系:
timing(clock, state, config)通过运行基于时间的动画,把state.position从当前位置更新到config.toValue决定的目标位置;动画预期持续config.duration毫秒,并使用config.easing指定的缓动函数(这里为Easing.inOut(Easing.ease))。state.frameTime表示动画已经持续的毫秒数(进度),state.time表示时钟最后一次被求值的时间;这两个变量在重启动画前都必须重置——这正是clockRunning为假分支中set(state.time, 0)、set(state.frameTime, 0)的原因。state.finished在position到达终值或frameTime超过duration时被置为1,于是末尾的cond(state.finished, stopClock(clock))会停止时钟,为下一次调用做好准备。
该模式的关键点
- 幂等与可复用:
clockRunning(clock)为真时只更新toValue(支持中途改变目标),为假时执行完整初始化。这使得同一个runTiming函数可以被重复调用而不产生状态残留。 - 返回值即动画值:
block最后返回state.position,因此整个动画表达式可以直接作为动画值使用,这正是声明式 API“节点即值”的体现。 - 配套使用的
timing/decay/spring:文档中说明,timing、decay、spring这类“动画步进节点”都可以用同样的时钟模式驱动,具体配置见 animations/timing.md、animations/decay.md 和 animations/spring.md。
与其他判断节点的配合
clockRunning返回0/1,因此在节点图中可与所有条件与逻辑节点无缝组合,例如:
- 作为
cond的条件:cond(clockRunning(clock), ...)分支执行。 - 与
not组合:not(clockRunning(clock))表示“时钟未运行”时执行。 - 与
and、or组合:构造“多个时钟均已启动”之类的复合条件。
它的行为与普通Value节点不同——普通值节点读取的是数据内容,而clockRunning读取的是时钟节点的运行状态,这是理解其返回值语义(1/0)的关键。
注意事项与边界行为
stopClock后时钟仍在更新:如前文所述,stopClock只是阻止依赖该时钟的节点继续求值,时钟自身时间戳仍会推进。因此clockRunning与“时钟当前时间”是两回事:前者表示下游求值链路是否启用,后者是时钟节点自身的值。- 保存停止时刻请用
Value:文档明确建议,若要保存时钟停止那一刻的时间戳,应把时钟值写入Value节点,而不是依赖stopClock或clockRunning的返回值。 - 重启动画前必须重置参数:参考
runTiming模式,state.time、state.frameTime、state.finished等状态值在时钟重新启动前需要显式复位,否则动画进度会残留。
版本与适用范围说明
本节点文档属于仓库中 versioned_docs/version-1.x 版本目录,对应 react-native-reanimated 1.x 的节点式 API 时代。在 1.x 中,动画通过Clock、startClock、stopClock、clockRunning、timing等节点组合声明式驱动;而从 2.x 开始,API 已演进为useSharedValue、withTiming等更简洁的写法。因此本文内容适用于仍在维护 1.x 代码库的读者,或希望理解 reanimated 时钟模型历史设计的读者。
更多节点可查阅 nodes 目录 下的对应文档(如startClock、stopClock、cond等),以及介绍时钟总体设计的 clock.md。
小结
clockRunning(clockNode)返回1表示时钟已启动并在逐帧更新,返回0表示未启动。- 它与
startClock、stopClock构成时钟生命周期控制三件套,通常作为cond的条件使用。 - 典型用法是
runTiming模式:时钟已运行时仅更新目标值,未运行时重置状态并启动时钟,动画结束后用stopClock收尾。 - 注意
stopClock不会停止时钟自身的时间戳推进,保存停止时刻需借助Value节点。
【免费下载链接】react-native-reanimatedReact Native's Animated library reimplemented项目地址: https://gitcode.com/GitHub_Trending/re/react-native-reanimated
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考