Skip to content

feat: report CSS animation events from the engine - #10070

Merged
MatiPl01 merged 11 commits into
mainfrom
@matipl01/css-events-cpp
Aug 12, 2026
Merged

MatiPl01 merged 11 commits into
mainfrom
@matipl01/css-events-cpp

Conversation

@MatiPl01

@MatiPl01 MatiPl01 commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Reports CSS animation events from the engine. The run lifecycle already tracks the milestones a run crosses, so this maps each one to its event and emits it with the time elapsed by then.

Which time that is comes from the phase change rather than the milestone, so a start entering from a delay reports the interval start and one re-entering a finished run reports the interval end. A run made infinite after it had finished reports an unbounded one, as on the web.

Subscription is a bitmask travelling with the animation config a view already sends, so a view without callbacks installs no reporter and emits nothing.

Loop-driven animations only, which is all of them today: platformAnimationFactory has no implementation on any platform.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2237dc0c-39b8-4ac8-a07c-64e4d675059a

📥 Commits

Reviewing files that changed from the base of the PR and between 543393e and cd45b9b.

📒 Files selected for processing (6)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.h
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSLoopAnimation.h
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.h

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added CSS animation event reporting for starts, iterations, completions, and cancellations.
    • Added configurable event masks to control which animation events are emitted.
    • Added elapsed-time information to reported animation events.
    • Animation cancellation is now reported when animations are replaced or removed.
  • Bug Fixes
    • Improved handling of paused animations to prevent unnecessary scheduling.
    • Improved timing accuracy for animation milestones, including delays, iterations, completion, and cancellation.

Walkthrough

CSS animations now parse event masks, calculate milestone elapsed times, report cancellations, and emit lifecycle events through the registry’s CSS event emitter.

Changes

CSS animation event reporting

Layer / File(s) Summary
Event contracts and animation interfaces
packages/react-native-reanimated/Common/cpp/reanimated/CSS/configs/*, packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimationObserver.h, packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.h, packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSLoopAnimation.h, packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h, packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.h
The animation updates include an eventMask. Shared observer, milestone reporter, progress, and registry interfaces carry animation event data.
Milestone timing and cancellation
packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.cpp, packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSLoopAnimation.cpp
Milestone elapsed times are calculated for start, iteration, end, and cancellation. Aborts record their timestamp. Paused animations update once without scheduling.
Animation event dispatch
packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.cpp, packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimationsGroup.*
Event masks control milestone callbacks. CSSAnimation maps enabled milestones to animation events and clears callbacks during destruction.
Registry event integration
packages/react-native-reanimated/Common/cpp/reanimated/CSS/registries/CSSAnimationsRegistry.cpp, packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp
The registry propagates event masks, reports cancellations for removed animations, and emits events through CSSEventsEmitter.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CSSAnimationsRegistry
  participant CSSAnimation
  participant CSSLoopAnimation
  participant AnimationProgressProvider
  participant CSSEventsEmitter
  CSSAnimationsRegistry->>CSSAnimation: apply eventMask
  CSSAnimation->>CSSLoopAnimation: register milestone reporter
  CSSLoopAnimation->>AnimationProgressProvider: observe lifecycle milestone
  AnimationProgressProvider->>CSSLoopAnimation: milestone and elapsed time
  CSSLoopAnimation->>CSSAnimation: forward milestone
  CSSAnimation->>CSSAnimationsRegistry: onAnimationEvent(metadata)
  CSSAnimationsRegistry->>CSSEventsEmitter: emit CSS animation event
Loading
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly explains CSS animation event reporting, milestone timing, event-mask subscriptions, and loop-driven animation scope.
Title check ✅ Passed The title clearly and concisely describes the main change: reporting CSS animation events from the engine.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch @matipl01/css-events-cpp

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@MatiPl01 MatiPl01 self-assigned this Jul 27, 2026
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from 99d90e6 to 5a9d10a Compare July 27, 2026 14:02
Base automatically changed from @matipl01/css-callback-store to main July 27, 2026 14:29
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from 35a39a9 to c4c7ce0 Compare July 27, 2026 14:43
@MatiPl01
MatiPl01 changed the base branch from main to @matipl01/animation-progress-changes July 27, 2026 14:43
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from c4c7ce0 to 9a490f0 Compare July 27, 2026 14:46
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from 9a490f0 to 4533be0 Compare July 28, 2026 07:06
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from 4533be0 to f8629d4 Compare July 28, 2026 09:05
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from f8629d4 to 5516178 Compare July 30, 2026 09:20
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch 2 times, most recently from 087444c to 0b8a27f Compare July 30, 2026 18:52
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch 4 times, most recently from caee059 to 81dbde5 Compare July 30, 2026 20:47
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch 3 times, most recently from 2aed068 to 98c10bb Compare July 31, 2026 07:07
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from ed37fc9 to 4c0d6ed Compare August 12, 2026 11:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h`:
- Line 84: Preserve the previously reached finite interval end when
CSSLoopAnimation::updateSettings() changes the iteration count to infinite, so
AnimationProgressProvider::intervalEnd() still reports that historical boundary
for the restarted animationstart event. Update the lifecycle timing state used
by RunLifecycle::reachPhase() rather than allowing the infinite configuration to
reset it to zero, and add a regression test covering a finished run followed by
setIterationCount(-1).
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e72d105d-e0cb-4dd6-b273-f7739c91954d

📥 Commits

Reviewing files that changed from the base of the PR and between 543393e and 4c0d6ed.

📒 Files selected for processing (1)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/progress/AnimationProgressProvider.h

MatiPl01 added a commit that referenced this pull request Aug 12, 2026
`RunLifecycle` tracks which phase a run is in and reports the milestones
each phase change crosses. It knows nothing about animations or
transitions: the owner maps `Created`, `Started`, `Ended`, `Repeated`
and `Aborted` to its own events.

The phase changes come straight from the tables in
[css-animations-2](https://drafts.csswg.org/css-animations-2/#event-dispatch)
and
[css-transitions-2](https://drafts.csswg.org/css-transitions-2/#event-dispatch),
which agree on every row. That includes the rows that move a run
backwards, which a style update reaches by re-timing a run that is
already going or already finished.

A milestone can be reached by more than one phase change, and the time
it carries differs: a start is the interval start entering from a delay
and the interval end re-entering after finishing. Hence `MilestoneTime`,
used by the owners in #10070 and #10120.

Pausing also moves out of the progress state, fixing an animation
created with `playState: Paused` reporting `animationstart` while still
paused and never once it actually started.

Nothing registers a reporter yet. First step of the CSS animation
callbacks series.
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from 4c0d6ed to 0f26e5b Compare August 12, 2026 12:31
Base automatically changed from @matipl01/css-events-transport to main August 12, 2026 13:40
A start reports the interval start when the run enters from its delay and
the interval end when it re-enters after finishing, so the milestone alone
no longer identifies which time to send.
A run dropped back an iteration by re-timing crosses the end of the
iteration it lands in, not the start.
A finished run that is given an infinite iteration count starts again,
and the clamp turned its interval end into zero. The web reports the
start with an infinite elapsed time there.
MatiPl01 added a commit that referenced this pull request Aug 12, 2026
The transport for CSS events: a buffer that batches whatever is emitted
and hands it to JS in one call, plus the JSI entrypoint that installs
the receiving function.

Nothing emits yet, so this is inert on its own. #10070 adds the
animation side.

Emitting buffers the event and asks the JS thread to drain, so however
many events land before the drain runs arrive in a single JS call. That
keeps the JS call off the UI thread and out of the registry lock. A
drain is requested per event rather than latched per batch: a request
can be dropped before it runs, and there is no signal for that, so
anything remembering that one is outstanding would strand the buffer for
good. `elapsedTime` is converted to seconds once, matching the contract
already shipped on web.

The event type ordinals are the bit indices of the subscription mask, so
the JS constants have to stay in sync with them. The four transition
values are reserved and unemitted until CSS transitions land.
@MatiPl01
MatiPl01 force-pushed the @matipl01/css-events-cpp branch from 174de44 to 86605c4 Compare August 12, 2026 13:40
The event carries seconds, so reusing elapsedTime for both meant the
same name changed units mid-chain. Also drops the Observer alias, which
had one user left, and an include nothing used.
@MatiPl01
MatiPl01 requested review from pawicao and tjzel August 12, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants