Skip to content

chore: deduplicate the CSS platform transition state machine - #10356

Draft
MatiPl01 wants to merge 7 commits into
mainfrom
@matipl01/css-platform-transition-dedup
Draft

chore: deduplicate the CSS platform transition state machine#10356
MatiPl01 wants to merge 7 commits into
mainfrom
@matipl01/css-platform-transition-dedup

Conversation

@MatiPl01

Copy link
Copy Markdown
Member

The CSS reversing and interruption bookkeeping was implemented twice, near-verbatim, once in REACSSPlatformTransitions.mm and once in CSSPlatformTransitions.cpp. getCurrentValue was identical line for line and the reversal block matched down to the comments, so a spec fix had to be made in two languages to land on both platforms.

This moves that state machine into CSSPlatformTransitionProxy and reduces the platform seam to start and stop. Two of the four hooks fall out with it: cssCanRouteProperty, where both platforms wired the same free function, and cssGetPlatformValue, which was a pure function of the hoisted state. Each backend is left with only genuinely platform-specific work.

It also stops CSSAnimation::updatePropertyRouting from reading IOS_CSS_CORE_ANIMATION in code compiled for every platform, so the two platform flags can be toggled independently.

No behavior change on either platform. Verified by compiling every touched translation unit for the Android NDK and the iOS SDK, and by compiling each platform with the other platform's flag omitted from REANIMATED_FEATURE_FLAGS (a constant-evaluated read of a missing flag is a compile error, so a clean build proves the flag is never read there), with the negative controls failing as expected.

Draft: on-device validation of the platform path with the flags on is still outstanding.

CSSAnimation::updatePropertyRouting read IOS_CSS_CORE_ANIMATION directly, from
code compiled for every platform, so an Android build with that flag on would
have taken the branch too. It is inert today only because no platform supplies
a CSSPlatformAnimationFactory and the null check runs first.

Move the read into canRouteCSSAnimations(), next to canRouteCSSProperty, which
already splits on the preprocessor so each platform reads only its own flag.
Common no longer names a platform flag outside a guard.
The reversing and interruption bookkeeping the CSS spec requires was written
twice, near-verbatim: once in REACSSPlatformTransitions.mm and once in
CSSPlatformTransitions.cpp. getCurrentValue was identical line for line, and
the reversal block matched down to the comments, so every spec fix had to be
made in two languages.

Hoist it into CSSPlatformTransitionProxy, which now owns the per-property
timeline, and shrink the platform seam to start and stop. Two of the four
hooks disappear with it: cssCanRouteProperty, where both platforms wired the
same free function, and cssGetPlatformValue, which was a pure function of the
hoisted state. The backends are left with only the work that is actually
platform specific, CoreAnimation on Apple and the interned-easing JNI seam on
Android.

No behavior change on either platform.
@MatiPl01 MatiPl01 self-assigned this Aug 20, 2026
@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

CSS animation routing now checks platform backend availability. CSSPlatformTransitionProxy owns active transition timelines and handles reversals, interruptions, current values, starts, and stops. Android and Apple backends now receive explicit timing and easing data. Native platform callback wiring now exposes CSS transition start and stop operations.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: moving duplicated CSS platform transition state into one shared state machine.
Description check ✅ Passed The description directly explains the deduplication, platform hook changes, flag handling, validation, and remaining on-device validation.
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.

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.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.mm (1)

67-76: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Align the backend start value with the proxy timeline. Bruce found the real smoking gun here: CSSPlatformTransitionProxy computes startValue for its timeline, but the callback receives fromValue, and this block can replace that value with the presentation value. getCurrentValue then retraces a different start value from the one used by CABasicAnimation.

🧹 Nitpick comments (2)
packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.cpp (1)

400-415: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider designated initializers for PlatformDepMethodsHolder.

This is positional aggregate initialization of a large holder whose members are guarded by platform #ifdef blocks in some builds. Two members changed in this PR. A future insertion between members of compatible callback types binds the wrong callback with no compile error.

The current signatures differ enough that a swap would not compile today, so this is not a defect. Designated initializers make each binding explicit and keep the Android and Apple initialization sites readable side by side.

♻️ Proposed refactor
   return {
-      requestRender,
-      preserveMountedTags,
-      synchronouslyUpdateUIPropsFunction,
-      getAnimationTimestamp,
-      registerSensorFunction,
-      unregisterSensorFunction,
-      setGestureStateFunction,
-      subscribeForKeyboardEventsFunction,
-      unsubscribeFromKeyboardEventsFunction,
-      maybeFlushUiUpdatesQueueFunction,
-      attachPseudoSelectorFunction,
-      detachPseudoSelectorFunction,
-      cssStartTransition,
-      cssStopTransition,
+      .requestRender = requestRender,
+      .preserveMountedTags = preserveMountedTags,
+      .synchronouslyUpdateUIPropsFunction = synchronouslyUpdateUIPropsFunction,
+      .getAnimationTimestamp = getAnimationTimestamp,
+      .registerSensorFunction = registerSensorFunction,
+      .unregisterSensorFunction = unregisterSensorFunction,
+      .setGestureStateFunction = setGestureStateFunction,
+      .subscribeForKeyboardEvents = subscribeForKeyboardEventsFunction,
+      .unsubscribeFromKeyboardEvents = unsubscribeFromKeyboardEventsFunction,
+      .maybeFlushUIUpdatesQueueFunction = maybeFlushUiUpdatesQueueFunction,
+      .attachPseudoSelector = attachPseudoSelectorFunction,
+      .detachPseudoSelector = detachPseudoSelectorFunction,
+      .cssStartTransition = cssStartTransition,
+      .cssStopTransition = cssStopTransition,
   };

Align the designator names with the declarations in PlatformDepMethodsHolder.h.

packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp (1)

59-86: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Document the interruption contract for CSSStartTransitionFunction.

Bruce thinks the real smoking gun here is the missing API contract. Both backends already use the live rendered value when a transition is active and use fromValue only for a fresh transition. State this rule in CSSPlatformTransitionProxy.h.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f320a77-e120-4c91-a784-19424438a220

📥 Commits

Reviewing files that changed from the base of the PR and between f41b838 and 9b838db.

📒 Files selected for processing (13)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/CSSAnimation.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/platform.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/platform.h
  • packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/Tools/PlatformDepMethodsHolder.h
  • packages/react-native-reanimated/android/src/main/cpp/reanimated/android/CSS/CSSPlatformTransitions.cpp
  • packages/react-native-reanimated/android/src/main/cpp/reanimated/android/CSS/CSSPlatformTransitions.h
  • packages/react-native-reanimated/android/src/main/cpp/reanimated/android/NativeProxy.cpp
  • packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.h
  • packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.mm
  • packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

id toId = idFromPlatformValue(toValue);
double durationSec = durationMs / 1000.0;
CFTimeInterval beginTime = startTimeMs / 1000.0;
CFTimeInterval beginTime = startTimestampMs / 1000.0;

@coderabbitai coderabbitai Bot Aug 20, 2026

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect the slow-animations timestamp transform to confirm the clock basis.
set -euo pipefail

fd -a 'REASlowAnimations.(h|mm)' packages/react-native-reanimated/apple --exec rg -n -C 6 'calculateTimestampWithSlowAnimations|slowAnimation'

Repository: software-mansion/react-native-reanimated

Length of output: 977


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- slow-animation timestamp implementation ---'
fd -a -i 'REASlowAnimations' packages/react-native-reanimated/apple --exec sh -c 'for f do echo "### $f"; cat -n "$f"; done' sh

echo '--- timestamp producer ---'
cat -n packages/react-native-reanimated/apple/reanimated/apple/native/PlatformDepMethodsHolderImpl.mm | sed -n '55,82p'

echo '--- transition consumer ---'
cat -n packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.mm | sed -n '35,92p'

echo '--- related timestamp and convertTime usage ---'
rg -n -C 4 'getAnimationTimestamp|convertTime:.*fromLayer|startTimestampMs|calculateTimestampWithSlowAnimations' packages/react-native-reanimated/apple

Repository: software-mansion/react-native-reanimated

Length of output: 23746


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- all transition call sites and timestamp plumbing ---'
rg -n -C 8 'startTransitionForTag|startTimestampMs|StartTransition|startTransition' packages/react-native-reanimated | head -n 500

echo '--- timestamp API declarations and consumers ---'
rg -n -C 6 'GetAnimationTimestampFunction|getAnimationTimestamp|animationTimestamp|timestampMs' packages/react-native-reanimated/src packages/react-native-reanimated/Common packages/react-native-reanimated/apple 2>/dev/null | head -n 500

echo '--- transition implementation outline and header ---'
ast-grep outline packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.mm --view compact
cat -n packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.h | sed -n '18,38p'

Repository: software-mansion/react-native-reanimated

Length of output: 50397


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- CSS transition source files ---'
fd -a -t f . packages/react-native-reanimated/Common/cpp | rg '/CSS/|CSSTransition|CSSPlatform'

echo '--- start timestamp assignments in shared CSS code ---'
rg -n -C 5 'startTimestampMs|startTimestamp|animationTimestamp' \
  packages/react-native-reanimated/Common/cpp/reanimated/CSS \
  packages/react-native-reanimated/Common/cpp/reanimated 2>/dev/null | head -n 400

echo '--- relevant shared transition implementations ---'
for f in $(fd -a -t f . packages/react-native-reanimated/Common/cpp | rg '/CSS/|CSSTransition|CSSPlatform'); do
  if rg -q 'startTimestampMs|startTimestamp|animationTimestamp' "$f"; then
    echo "### $f"
    rg -n -C 10 'startTimestampMs|startTimestamp|animationTimestamp' "$f"
  fi
done

Repository: software-mansion/react-native-reanimated

Length of output: 50396


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- animation timestamp callback consumers ---'
rg -n -C 8 'getAnimationTimestamp|animationTimestampFunction|frameTimestamp|resolveTimestamp\(' \
  packages/react-native-reanimated/Common/cpp \
  packages/react-native-reanimated/apple/reanimated/apple/native \
  packages/react-native-reanimated/apple/reanimated/apple 2>/dev/null | head -n 500

echo '--- transition routing and timestamp origin ---'
cat -n packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp | sed -n '1,95p'
rg -n -C 8 'getAnimationTimestamp|resolveTimestamp' packages/react-native-reanimated/Common/cpp/reanimated/Fabric packages/react-native-reanimated/Common/cpp/reanimated/NativeModules packages/react-native-reanimated/Common/cpp/reanimated | head -n 500

echo '--- deterministic clock-basis probe ---'
python3 - <<'PY'
anchor = 100.0
real_start = 110.0
drag_coef = 10.0
scaled_start = anchor + (real_start - anchor) / drag_coef
for real_now in (110.0, 111.0, 120.0):
    scaled_now = anchor + (real_now - anchor) / drag_coef
    print(f"real={real_now:.1f}s scaled={scaled_now:.1f}s delta={real_now-scaled_now:.1f}s")
print(f"beginTime passed to CoreAnimation: {scaled_start:.1f}s")
PY

Repository: software-mansion/react-native-reanimated

Length of output: 50397


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- OperationsLoop timestamp resolution ---'
cat -n packages/react-native-reanimated/Common/cpp/reanimated/Fabric/updates/OperationsLoop.cpp | sed -n '1,75p'
cat -n packages/react-native-reanimated/Common/cpp/reanimated/Fabric/updates/OperationsLoop.h | sed -n '1,55p'

echo '--- native transition call with resolved timestamp ---'
cat -n packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp | sed -n '45,90p'

echo '--- clock-basis calculation ---'
python3 - <<'PY'
anchor = 100.0
drag = 10.0
real_start = 110.0
scaled_start = anchor + (real_start - anchor) / drag
real_now = 111.0
scaled_now = anchor + (real_now - anchor) / drag
print({
    "real_start": real_start,
    "scaled_start": scaled_start,
    "real_now": real_now,
    "scaled_now": scaled_now,
    "real_elapsed": real_now - real_start,
    "scaled_elapsed": scaled_now - scaled_start,
    "begin_time_error": real_start - scaled_start,
})
PY

Repository: software-mansion/react-native-reanimated

Length of output: 7628


Use an absolute Core Animation timestamp for beginTime.

Bruce found the real smoking gun here: startTimestampMs uses the slow-animation-scaled clock, but [layer convertTime:fromLayer:] expects absolute Core Animation time. Convert it to the Core Animation clock before assigning anim.beginTime; otherwise slow animations seek to the wrong position.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Valid finding, but it predates this PR and it is simulator-only, so I am not fixing it here.

Provenance: the convertTime: line is byte-identical to the branch base (f41b838) and came from #9507; the scaled clock producer came from #7733. This PR moved the reversing state machine out of this file, the beginTime line never left it.

Scope: the warp lives entirely inside #if TARGET_IPHONE_SIMULATOR in REASlowAnimations.mm, and getUIAnimationDragCoefficient's dlsym is inside the same guard, so on device the function returns its input unchanged and the coefficient is 1. No real device can hit this.

Worth recording that the impact on the simulator is worse than a timing skew: the error is (t - T0) * (1 - 1/k), which at k=10 grows about 0.9s per real second from the moment slow animations are toggled, so within a second or so every platform-routed transition has a beginTime far enough in the past that CoreAnimation treats it as already finished, and the property snaps.

I am not applying the fix blind. beginTime is wrong under either hypothesis, but the correct correction depends on whether the simulator slows CAAnimations natively via the drag coefficient. If it does, only the absolute timestamp needs remapping and anim.duration is already right; if it does not, the platform path also plays k times faster than the loop path for the same transition. That wants an experiment on a simulator with slow animations toggled, not a guess, so it is going in its own PR.

On the other two comments in this review, which I cannot reply to inline: passing startValue to startTransition_ instead of fromValue would be a regression, not a fix. The player's live presentation-layer read and the engine's retrace are independent by design; substituting the retrace replaces a true presentation value with a model-side approximation and reintroduces the snap the comment above anim.fromValue warns about. Both are also unchanged from main.

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.

@MatiPl01, understood. The real smoking gun here is simulator-only and predates #10356. The separate simulator experiment is necessary before selecting a correction for beginTime and possibly anim.duration.

The virtual smoking gun here is the distinction between fromValue and the live presentation-layer value. startTransition_ should continue to receive fromValue. The backend must continue to read the presentation layer when it replaces an active animation.

Would you like me to open a GitHub follow-up issue for the simulator slow-animation investigation?


✏️ Learnings added
Learnt from: MatiPl01
Repo: software-mansion/react-native-reanimated PR: 10356
File: packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.mm:53-53
Timestamp: 2026-08-20T21:27:57.616Z
Learning: In the Apple CSS transition backend, `REACSSPlatformTransitions.mm` must pass the engine-provided `fromValue` to `startTransition_` while independently reading `CALayer.presentationLayer` when replacing an active animation. The engine retrace value and the live presentation-layer value have different purposes; replacing `fromValue` with the retrace value can reintroduce visual snapping.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

The old proxy returned `applyTransition_ && applyTransition_(...)`, so a
platform that wired no apply hook kept every property on the loop. The hoist
dropped that check. Nothing can reach it today, since canRoute() already gates
on the same hook and only a successful apply puts a property in
routing.platform, but the pseudo-selector path calls apply() off routing state
rather than off canRoute(), so restore the guard rather than rely on that.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp (1)

73-82: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass the reconstructed current value to startTransition_.

Bruce found the real smoking gun here: apply computes startValue from the stored timeline, but startTransition_ still receives fromValue. During an interruption or reversal, fromValue can be stale. The native transition can then jump because it starts from a value different from the proxy timeline.

Pass startValue when available. Keep fromValue as the fallback. Add regression coverage for interruptions and reversals.

Proposed fix
+  const PlatformValue &transitionFromValue = startValue ? *startValue : fromValue;
+
   if (!startTransition_ ||
       !startTransition_(
           viewTag,
           propertyName,
-          fromValue,
+          transitionFromValue,
           toValue,
           reversing.duration,

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 937b672c-ef98-4b9c-a209-1c9fd6df863a

📥 Commits

Reviewing files that changed from the base of the PR and between 9b838db and 186b67e.

📒 Files selected for processing (1)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.

The seam typedefs already explain what startTimestampMs and persistent mean, so
both backends were restating it, and three files carried a near-identical line
about cancelling. Keep the per-backend docs to what only that backend knows,
and bail out of apply() before the reversing work when there is no start hook
rather than inside the call.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp (1)

77-85: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass the computed live value to the native start callback.

Bruce found the real smoking gun here. Lines 68-75 compute startValue from the active timeline, but this call passes fromValue. During an interruption or reversal, the native transition can jump to the stale diff value instead of continuing from the value on screen.

Proposed fix
+  const auto platformStartValue = startValue.value_or(fromValue);
   if (!startTransition_(
           viewTag,
           propertyName,
-          fromValue,
+          platformStartValue,
           toValue,

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bbcb63af-77d2-4f11-b656-fd4b5c152309

📥 Commits

Reviewing files that changed from the base of the PR and between 47cbbac and 0034141.

📒 Files selected for processing (6)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/platform.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/platform.h
  • packages/react-native-reanimated/android/src/main/cpp/reanimated/android/CSS/CSSPlatformTransitions.h
  • packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.h
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/platform.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/platform.h
  • packages/react-native-reanimated/android/src/main/cpp/reanimated/android/CSS/CSSPlatformTransitions.h
  • packages/react-native-reanimated/apple/reanimated/apple/CSS/REACSSPlatformTransitions.h

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

canRoute only checked for a start hook while remove checked for a stop hook, so
a backend that supplied one and not the other would have routed properties it
could never cancel. Require both, and restore the note that a settings-only
config leaves the stored settings a revision behind for the toggle path, which
was lost when the state machine moved.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp (2)

117-120: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle delayed zero-duration transitions

Bruce thinks the real smoking gun here is the duration == 0 branch. makeReversingState stores timestamp + delay in startTimestamp, but getCurrentValue returns progress 1.0 before that timestamp. An interruption during a positive delay therefore reads adjustedEnd instead of startValue.

Return 0.0 before reversing.startTimestamp and 1.0 at or after it when reversing.duration == 0. Add a test for interruption during a positive delay.


233-238: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Preserve non-scalar resume values.

Bruce found the real smoking gun here. Apple routing accepts color and size properties, but getResumeValue() retains only the double alternative. When these transitions move to the loop, resumeFrom is empty and the original fromValue is reused, which can cause a visible jump.

Preserve the complete PlatformValue for both migration paths, or restrict routing to scalar properties. Add tests for routed color and size properties.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6ebf7382-9168-45d3-a868-b6f2219b6193

📥 Commits

Reviewing files that changed from the base of the PR and between 0034141 and 4290ddd.

📒 Files selected for processing (2)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.cpp
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/react-native-reanimated/Common/cpp/reanimated/CSS/core/transition/CSSPlatformTransitionProxy.h

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.

reverseShorten and the proxy's retrace both answer "how far has this
ReversingState played at t, eased", off the same fields, about ninety lines
apart. Name it once. The shared form is the retrace's guarded one, because a
shortening factor driven negative by an overshooting easing can store a
negative duration, and clamping to a hi below lo is a precondition violation
reverseShorten only got away with by discarding the result.

Also correct the retrace's doc: it returns nullopt when the property is not
routed, not after a non-reversing interruption. active_ is keyed by property
name, so the start and end values always hold the same alternative and the lerp
cannot fail.
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.

1 participant