diff --git a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js
index 1a01d2fe500..1fe2ceff6dc 100644
--- a/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js
+++ b/packages/react-native/Libraries/Animated/__tests__/Animated-itest.js
@@ -229,12 +229,16 @@ test('moving box by 50 points with offset 10', () => {
expect(viewElement.getBoundingClientRect().x).toBe(0);
+ let finishValue = null;
+
Fantom.runTask(() => {
Animated.timing(_translateX, {
toValue: 50,
duration: 1000, // 1 second
useNativeDriver: true,
- }).start();
+ }).start(result => {
+ finishValue = result;
+ });
});
Fantom.runTask(() => {
@@ -260,6 +264,18 @@ test('moving box by 50 points with offset 10', () => {
.translateX,
).toBeCloseTo(60, 0.001);
+ expect(root.getRenderedOutput({props: ['transform']}).toJSX()).toEqual(
+ ,
+ );
+
// TODO: this shouldn't be neccessary but C++ Animated still schedules a React state update.
Fantom.runWorkLoop();
+
+ expect(root.getRenderedOutput({props: ['transform']}).toJSX()).toEqual(
+ , // // must include offset.
+ );
+
+ expect(finishValue?.finished).toBe(true);
+ expect(finishValue?.value).toBe(50); // must not include offset.
+ expect(finishValue?.offset).toBe(10);
});
diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp
index ec5dda3f049..ebdc9a943cc 100644
--- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp
+++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/AnimatedModule.cpp
@@ -241,7 +241,7 @@ void AnimatedModule::executeOperation(const Operation& operation) {
nodesManager_->disconnectAnimatedNodes(op.parentTag, op.childTag);
} else if constexpr (std::is_same_v) {
nodesManager_->startAnimatingNode(
- op.animationId, op.nodeTag, op.config, op.endCallback);
+ op.animationId, op.nodeTag, op.config, std::move(op.endCallback));
} else if constexpr (std::is_same_v) {
nodesManager_->stopAnimation(op.animationId, false);
} else if constexpr (std::is_same_v) {
diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp
index e4f2f40d315..cc65a9e6bc3 100644
--- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp
+++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.cpp
@@ -274,7 +274,7 @@ void NativeAnimatedNodesManager::startAnimatingNode(
int animationId,
Tag animatedNodeTag,
const folly::dynamic& config,
- const std::optional& endCallback) noexcept {
+ std::optional endCallback) noexcept {
if (auto iter = activeAnimations_.find(animationId);
iter != activeAnimations_.end()) {
// reset animation config
@@ -288,15 +288,27 @@ void NativeAnimatedNodesManager::startAnimatingNode(
switch (typeEnum.value()) {
case AnimationDriverType::Frames: {
animation = std::make_unique(
- animationId, animatedNodeTag, endCallback, config, this);
+ animationId,
+ animatedNodeTag,
+ std::move(endCallback),
+ config,
+ this);
} break;
case AnimationDriverType::Spring: {
animation = std::make_unique(
- animationId, animatedNodeTag, endCallback, config, this);
+ animationId,
+ animatedNodeTag,
+ std::move(endCallback),
+ config,
+ this);
} break;
case AnimationDriverType::Decay: {
animation = std::make_unique(
- animationId, animatedNodeTag, endCallback, config, this);
+ animationId,
+ animatedNodeTag,
+ std::move(endCallback),
+ config,
+ this);
} break;
}
if (animation) {
diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h
index be83cf76aae..883b70c8e71 100644
--- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h
+++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/NativeAnimatedNodesManager.h
@@ -104,7 +104,7 @@ class NativeAnimatedNodesManager {
int animationId,
Tag animatedNodeTag,
const folly::dynamic& config,
- const std::optional& endCallback) noexcept;
+ std::optional endCallback) noexcept;
void stopAnimation(
int animationId,
diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp
index edb0463e15f..2ed62c882c5 100644
--- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp
+++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.cpp
@@ -51,16 +51,13 @@ void AnimationDriver::startAnimation() {
}
void AnimationDriver::stopAnimation(bool /*ignoreCompletedHandlers*/) {
- std::optional value = std::nullopt;
if (auto node =
- manager_->getAnimatedNode(animatedValueTag_)) {
- value = node->getValue();
- } else {
- LOG(ERROR)
- << "animatedValueTag should be associated with a ValueAnimatedNode";
- }
- if (endCallback_) {
- endCallback_.value().call({.finished = true, .value = value});
+ manager_->getAnimatedNode(animatedValueTag_);
+ endCallback_) {
+ endCallback_.value().call(
+ {.finished = true,
+ .value = node->getRawValue(),
+ .offset = node->getOffset()});
}
}
diff --git a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h
index fdf07cd84f0..5d810ae2b21 100644
--- a/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h
+++ b/packages/react-native/ReactCxxPlatform/react/renderer/animated/drivers/AnimationDriver.h
@@ -42,10 +42,6 @@ class AnimationDriver {
return animatedValueTag_;
}
- inline std::optional endCallback() noexcept {
- return endCallback_;
- }
-
bool getIsComplete() const noexcept {
return isComplete_;
}