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_; }