diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m index 2eed4b0a617..e69b2d5829e 100644 --- a/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m +++ b/Libraries/NativeAnimation/RCTNativeAnimatedNodesManager.m @@ -170,6 +170,7 @@ RCTLogError(@"Not a value node."); return; } + [self stopAnimationsForNode:node]; RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node; valueNode.value = value.floatValue; @@ -264,6 +265,20 @@ } } +- (void)stopAnimationsForNode:(nonnull RCTAnimatedNode *)node +{ + NSMutableArray> *discarded = [NSMutableArray new]; + for (id driver in _activeAnimations) { + if ([driver.valueNode isEqual:node]) { + [discarded addObject:driver]; + } + } + for (id driver in discarded) { + [driver stopAnimation]; + [_activeAnimations removeObject:driver]; + } +} + #pragma mark -- Events - (void)addAnimatedEventToView:(nonnull NSNumber *)viewTag @@ -328,6 +343,7 @@ NSMutableArray *driversForKey = _eventDrivers[key]; if (driversForKey) { for (RCTEventAnimation *driver in driversForKey) { + [self stopAnimationsForNode:driver.valueNode]; [driver updateWithEvent:event]; } diff --git a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java index 6251b8f4dd5..9ed40b82368 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java @@ -29,7 +29,6 @@ import com.facebook.react.uimanager.events.EventDispatcherListener; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -145,6 +144,7 @@ import javax.annotation.Nullable; throw new JSApplicationIllegalArgumentException("Animated node with tag " + tag + " does not exists or is not a 'value' node"); } + stopAnimationsForNode(node); ((ValueAnimatedNode) node).mValue = value; mUpdatedNodes.put(tag, node); } @@ -208,6 +208,24 @@ import javax.annotation.Nullable; mActiveAnimations.put(animationId, animation); } + private void stopAnimationsForNode(AnimatedNode animatedNode) { + // in most of the cases there should never be more than a few active animations running at the + // same time. Therefore it does not make much sense to create an animationId -> animation + // object map that would require additional memory just to support the use-case of stopping + // an animation + for (int i = 0; i < mActiveAnimations.size(); i++) { + AnimationDriver animation = mActiveAnimations.valueAt(i); + if (animatedNode.equals(animation.mAnimatedValue)) { + // Invoke animation end callback with {finished: false} + WritableMap endCallbackResponse = Arguments.createMap(); + endCallbackResponse.putBoolean("finished", false); + animation.mEndCallback.invoke(endCallbackResponse); + mActiveAnimations.removeAt(i); + i--; + } + } + } + public void stopAnimation(int animationId) { // in most of the cases there should never be more than a few active animations running at the // same time. Therefore it does not make much sense to create an animationId -> animation @@ -362,6 +380,7 @@ import javax.annotation.Nullable; List driversForKey = mEventDrivers.get(event.getViewTag() + eventName); if (driversForKey != null) { for (EventAnimationDriver driver : driversForKey) { + stopAnimationsForNode(driver.mValueNode); event.dispatch(driver); mRunUpdateNodeList.add(driver.mValueNode); }