mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
d1c3c2d084
Summary: The Animated native module cannot be converted to a TM because it has perf regressions when used in conjunction with Paper renderer. Instead of fixing these complicated perf issues (which will disappear when Fabric ships this half), temporarily fork the native module. The new fork is converted to a TM, and only used to unblock bridgeless mode testing. Changelog:[Internal] Reviewed By: RSNara Differential Revision: D22651010 fbshipit-source-id: 912123ef38ac8c66025b7bba34a65ec6d98f330d
353 lines
11 KiB
Plaintext
353 lines
11 KiB
Plaintext
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
|
#import <React/RCTNativeAnimatedTurboModule.h>
|
|
#import <React/RCTNativeAnimatedNodesManager.h>
|
|
|
|
#import <RCTTypeSafety/RCTConvertHelpers.h>
|
|
|
|
#import "RCTAnimationPlugins.h"
|
|
|
|
typedef void (^AnimatedOperation)(RCTNativeAnimatedNodesManager *nodesManager);
|
|
|
|
@interface RCTNativeAnimatedTurboModule() <NativeAnimatedModuleSpec>
|
|
@end
|
|
|
|
@implementation RCTNativeAnimatedTurboModule
|
|
{
|
|
RCTNativeAnimatedNodesManager *_nodesManager;
|
|
|
|
// Operations called after views have been updated.
|
|
NSMutableArray<AnimatedOperation> *_operations;
|
|
// Operations called before views have been updated.
|
|
NSMutableArray<AnimatedOperation> *_preOperations;
|
|
NSMutableDictionary<NSNumber *, NSNumber *> *_animIdIsManagedByFabric;
|
|
}
|
|
|
|
RCT_EXPORT_MODULE();
|
|
|
|
- (void)invalidate
|
|
{
|
|
[_nodesManager stopAnimationLoop];
|
|
[self.bridge.eventDispatcher removeDispatchObserver:self];
|
|
[self.bridge.uiManager.observerCoordinator removeObserver:self];
|
|
[self.bridge.surfacePresenter removeObserver:self];
|
|
}
|
|
|
|
- (dispatch_queue_t)methodQueue
|
|
{
|
|
// This module needs to be on the same queue as the UIManager to avoid
|
|
// having to lock `_operations` and `_preOperations` since `uiManagerWillPerformMounting`
|
|
// will be called from that queue.
|
|
return RCTGetUIManagerQueue();
|
|
}
|
|
|
|
- (void)setBridge:(RCTBridge *)bridge
|
|
{
|
|
[super setBridge:bridge];
|
|
|
|
_nodesManager = [[RCTNativeAnimatedNodesManager alloc] initWithBridge:self.bridge];
|
|
_operations = [NSMutableArray new];
|
|
_preOperations = [NSMutableArray new];
|
|
_animIdIsManagedByFabric = [NSMutableDictionary new];
|
|
|
|
[bridge.eventDispatcher addDispatchObserver:self];
|
|
[bridge.uiManager.observerCoordinator addObserver:self];
|
|
[bridge.surfacePresenter addObserver:self];
|
|
}
|
|
|
|
#pragma mark -- API
|
|
|
|
RCT_EXPORT_METHOD(createAnimatedNode:(double)tag
|
|
config:(NSDictionary<NSString *, id> *)config)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager createAnimatedNode:[NSNumber numberWithDouble:tag] config:config];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(connectAnimatedNodes:(double)parentTag
|
|
childTag:(double)childTag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager connectAnimatedNodes:[NSNumber numberWithDouble:parentTag] childTag:[NSNumber numberWithDouble:childTag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(disconnectAnimatedNodes:(double)parentTag
|
|
childTag:(double)childTag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager disconnectAnimatedNodes:[NSNumber numberWithDouble:parentTag] childTag:[NSNumber numberWithDouble:childTag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(startAnimatingNode:(double)animationId
|
|
nodeTag:(double)nodeTag
|
|
config:(NSDictionary<NSString *, id> *)config
|
|
endCallback:(RCTResponseSenderBlock)callBack)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager startAnimatingNode:[NSNumber numberWithDouble:animationId] nodeTag:[NSNumber numberWithDouble:nodeTag] config:config endCallback:callBack];
|
|
}];
|
|
|
|
RCTExecuteOnMainQueue(^{
|
|
if (![self->_nodesManager isNodeManagedByFabric:[NSNumber numberWithDouble:nodeTag]]) {
|
|
return;
|
|
}
|
|
|
|
RCTExecuteOnUIManagerQueue(^{
|
|
self->_animIdIsManagedByFabric[[NSNumber numberWithDouble:animationId]] = @YES;
|
|
[self flushOperationQueues];
|
|
});
|
|
});
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(stopAnimation:(double)animationId)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager stopAnimation:[NSNumber numberWithDouble:animationId]];
|
|
}];
|
|
if ([_animIdIsManagedByFabric[[NSNumber numberWithDouble:animationId]] boolValue]) {
|
|
[self flushOperationQueues];
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(setAnimatedNodeValue:(double)nodeTag
|
|
value:(double)value)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager setAnimatedNodeValue:[NSNumber numberWithDouble:nodeTag] value:[NSNumber numberWithDouble:value]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(setAnimatedNodeOffset:(double)nodeTag
|
|
offset:(double)offset)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager setAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag] offset:[NSNumber numberWithDouble:offset]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(flattenAnimatedNodeOffset:(double)nodeTag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager flattenAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(extractAnimatedNodeOffset:(double)nodeTag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager extractAnimatedNodeOffset:[NSNumber numberWithDouble:nodeTag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(connectAnimatedNodeToView:(double)nodeTag
|
|
viewTag:(double)viewTag)
|
|
{
|
|
NSString *viewName = [self.bridge.uiManager viewNameForReactTag:[NSNumber numberWithDouble:viewTag]];
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager connectAnimatedNodeToView:[NSNumber numberWithDouble:nodeTag] viewTag:[NSNumber numberWithDouble:viewTag] viewName:viewName];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView:(double)nodeTag
|
|
viewTag:(double)viewTag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager disconnectAnimatedNodeFromView:[NSNumber numberWithDouble:nodeTag] viewTag:[NSNumber numberWithDouble:viewTag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(restoreDefaultValues:(double)nodeTag)
|
|
{
|
|
[self addPreOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager restoreDefaultValues:[NSNumber numberWithDouble:nodeTag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(dropAnimatedNode:(double)tag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager dropAnimatedNode:[NSNumber numberWithDouble:tag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue:(double)tag)
|
|
{
|
|
__weak id<RCTValueAnimatedNodeObserver> valueObserver = self;
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager startListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag] valueObserver:valueObserver];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(double)tag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager stopListeningToAnimatedNodeValue:[NSNumber numberWithDouble:tag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(addAnimatedEventToView:(double)viewTag
|
|
eventName:(nonnull NSString *)eventName
|
|
eventMapping:(JS::NativeAnimatedModule::EventMapping &)eventMapping)
|
|
{
|
|
NSMutableDictionary *eventMappingDict = [NSMutableDictionary new];
|
|
eventMappingDict[@"nativeEventPath"] = RCTConvertVecToArray(eventMapping.nativeEventPath());
|
|
|
|
if (eventMapping.animatedValueTag()) {
|
|
eventMappingDict[@"animatedValueTag"] = @(*eventMapping.animatedValueTag());
|
|
}
|
|
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager addAnimatedEventToView:[NSNumber numberWithDouble:viewTag] eventName:eventName eventMapping:eventMappingDict];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(removeAnimatedEventFromView:(double)viewTag
|
|
eventName:(nonnull NSString *)eventName
|
|
animatedNodeTag:(double)animatedNodeTag)
|
|
{
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager removeAnimatedEventFromView:[NSNumber numberWithDouble:viewTag] eventName:eventName animatedNodeTag:[NSNumber numberWithDouble:animatedNodeTag]];
|
|
}];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getValue:(double)nodeTag saveValueCallback:(RCTResponseSenderBlock)saveValueCallback) {
|
|
[self addOperationBlock:^(RCTNativeAnimatedNodesManager *nodesManager) {
|
|
[nodesManager getValue:[NSNumber numberWithDouble:nodeTag] saveCallback:saveValueCallback];
|
|
}];
|
|
}
|
|
|
|
#pragma mark -- Batch handling
|
|
|
|
- (void)addOperationBlock:(AnimatedOperation)operation
|
|
{
|
|
[_operations addObject:operation];
|
|
}
|
|
|
|
- (void)addPreOperationBlock:(AnimatedOperation)operation
|
|
{
|
|
[_preOperations addObject:operation];
|
|
}
|
|
|
|
- (void)flushOperationQueues
|
|
{
|
|
if (_preOperations.count == 0 && _operations.count == 0) {
|
|
return;
|
|
}
|
|
NSArray<AnimatedOperation> *preOperations = _preOperations;
|
|
NSArray<AnimatedOperation> *operations = _operations;
|
|
_preOperations = [NSMutableArray new];
|
|
_operations = [NSMutableArray new];
|
|
|
|
|
|
RCTExecuteOnMainQueue(^{
|
|
for (AnimatedOperation operation in preOperations) {
|
|
operation(self->_nodesManager);
|
|
}
|
|
for (AnimatedOperation operation in operations) {
|
|
operation(self->_nodesManager);
|
|
}
|
|
[self->_nodesManager updateAnimations];
|
|
});
|
|
}
|
|
|
|
#pragma mark - RCTSurfacePresenterObserver
|
|
|
|
- (void)willMountComponentsWithRootTag:(NSInteger)rootTag
|
|
{
|
|
RCTAssertMainQueue();
|
|
RCTExecuteOnUIManagerQueue(^{
|
|
NSArray<AnimatedOperation> *preOperations = self->_preOperations;
|
|
self->_preOperations = [NSMutableArray new];
|
|
|
|
RCTExecuteOnMainQueue(^{
|
|
for (AnimatedOperation preOperation in preOperations) {
|
|
preOperation(self->_nodesManager);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
- (void)didMountComponentsWithRootTag:(NSInteger)rootTag
|
|
{
|
|
RCTAssertMainQueue();
|
|
RCTExecuteOnUIManagerQueue(^{
|
|
NSArray<AnimatedOperation> *operations = self->_operations;
|
|
self->_operations = [NSMutableArray new];
|
|
|
|
RCTExecuteOnMainQueue(^{
|
|
for (AnimatedOperation operation in operations) {
|
|
operation(self->_nodesManager);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
#pragma mark - RCTUIManagerObserver
|
|
|
|
- (void)uiManagerWillPerformMounting:(RCTUIManager *)uiManager
|
|
{
|
|
if (_preOperations.count == 0 && _operations.count == 0) {
|
|
return;
|
|
}
|
|
|
|
NSArray<AnimatedOperation> *preOperations = _preOperations;
|
|
NSArray<AnimatedOperation> *operations = _operations;
|
|
_preOperations = [NSMutableArray new];
|
|
_operations = [NSMutableArray new];
|
|
|
|
[uiManager prependUIBlock:^(__unused RCTUIManager *manager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
|
for (AnimatedOperation operation in preOperations) {
|
|
operation(self->_nodesManager);
|
|
}
|
|
}];
|
|
[uiManager addUIBlock:^(__unused RCTUIManager *manager, __unused NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
|
for (AnimatedOperation operation in operations) {
|
|
operation(self->_nodesManager);
|
|
}
|
|
|
|
[self->_nodesManager updateAnimations];
|
|
}];
|
|
}
|
|
|
|
#pragma mark -- Events
|
|
|
|
- (NSArray<NSString *> *)supportedEvents
|
|
{
|
|
return @[@"onAnimatedValueUpdate"];
|
|
}
|
|
|
|
- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value
|
|
{
|
|
[self sendEventWithName:@"onAnimatedValueUpdate"
|
|
body:@{@"tag": node.nodeTag, @"value": @(value)}];
|
|
}
|
|
|
|
- (void)eventDispatcherWillDispatchEvent:(id<RCTEvent>)event
|
|
{
|
|
// Events can be dispatched from any queue so we have to make sure handleAnimatedEvent
|
|
// is run from the main queue.
|
|
RCTExecuteOnMainQueue(^{
|
|
[self->_nodesManager handleAnimatedEvent:event];
|
|
});
|
|
}
|
|
|
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
{
|
|
return std::make_shared<facebook::react::NativeAnimatedModuleSpecJSI>(params);
|
|
}
|
|
|
|
@end
|
|
|
|
Class RCTNativeAnimatedTurboModuleCls(void) {
|
|
return RCTNativeAnimatedTurboModule.class;
|
|
}
|