react-native/Libraries/NativeAnimation/RCTNativeAnimatedModule.m

407 lines
13 KiB
Mathematica
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTNativeAnimatedModule.h"
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTLog.h>
#import "RCTAdditionAnimatedNode.h"
#import "RCTAnimationDriver.h"
#import "RCTAnimationUtils.h"
#import "RCTDiffClampAnimatedNode.h"
#import "RCTDivisionAnimatedNode.h"
#import "RCTEventAnimation.h"
#import "RCTFrameAnimation.h"
#import "RCTInterpolationAnimatedNode.h"
#import "RCTModuloAnimatedNode.h"
#import "RCTMultiplicationAnimatedNode.h"
#import "RCTPropsAnimatedNode.h"
#import "RCTSpringAnimation.h"
#import "RCTStyleAnimatedNode.h"
#import "RCTTransformAnimatedNode.h"
#import "RCTValueAnimatedNode.h"
@implementation RCTNativeAnimatedModule
{
NSMutableDictionary<NSNumber *, RCTAnimatedNode *> *_animationNodes;
NSMutableDictionary<NSNumber *, id<RCTAnimationDriver>> *_animationDrivers;
NSMutableDictionary<NSString *, RCTEventAnimation *> *_eventAnimationDrivers;
NSMutableSet<id<RCTAnimationDriver>> *_activeAnimations;
NSMutableSet<id<RCTAnimationDriver>> *_finishedAnimations;
NSMutableSet<RCTValueAnimatedNode *> *_updatedValueNodes;
NSMutableSet<RCTPropsAnimatedNode *> *_propAnimationNodes;
CADisplayLink *_displayLink;
}
RCT_EXPORT_MODULE()
- (void)setBridge:(RCTBridge *)bridge
{
[super setBridge:bridge];
_animationNodes = [NSMutableDictionary new];
_animationDrivers = [NSMutableDictionary new];
_eventAnimationDrivers = [NSMutableDictionary new];
_activeAnimations = [NSMutableSet new];
_finishedAnimations = [NSMutableSet new];
_updatedValueNodes = [NSMutableSet new];
_propAnimationNodes = [NSMutableSet new];
[bridge.eventDispatcher addDispatchObserver:self];
}
- (void)dealloc
{
[self.bridge.eventDispatcher removeDispatchObserver:self];
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"onAnimatedValueUpdate"];
}
RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
config:(NSDictionary<NSString *, id> *)config)
{
static NSDictionary *map;
static dispatch_once_t mapToken;
dispatch_once(&mapToken, ^{
map = @{@"style" : [RCTStyleAnimatedNode class],
@"value" : [RCTValueAnimatedNode class],
@"props" : [RCTPropsAnimatedNode class],
@"interpolation" : [RCTInterpolationAnimatedNode class],
@"addition" : [RCTAdditionAnimatedNode class],
@"diffclamp": [RCTDiffClampAnimatedNode class],
@"division" : [RCTDivisionAnimatedNode class],
@"multiplication" : [RCTMultiplicationAnimatedNode class],
@"modulus" : [RCTModuloAnimatedNode class],
@"transform" : [RCTTransformAnimatedNode class]};
});
NSString *nodeType = [RCTConvert NSString:config[@"type"]];
Class nodeClass = map[nodeType];
if (!nodeClass) {
RCTLogError(@"Animated node type %@ not supported natively", nodeType);
return;
}
RCTAnimatedNode *node = [[nodeClass alloc] initWithTag:tag config:config];
_animationNodes[tag] = node;
if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[_propAnimationNodes addObject:(RCTPropsAnimatedNode *)node];
}
}
RCT_EXPORT_METHOD(connectAnimatedNodes:(nonnull NSNumber *)parentTag
childTag:(nonnull NSNumber *)childTag)
{
RCTAssertParam(parentTag);
RCTAssertParam(childTag);
RCTAnimatedNode *parentNode = _animationNodes[parentTag];
RCTAnimatedNode *childNode = _animationNodes[childTag];
RCTAssertParam(parentNode);
RCTAssertParam(childNode);
[parentNode addChild:childNode];
}
RCT_EXPORT_METHOD(disconnectAnimatedNodes:(nonnull NSNumber *)parentTag
childTag:(nonnull NSNumber *)childTag)
{
RCTAssertParam(parentTag);
RCTAssertParam(childTag);
RCTAnimatedNode *parentNode = _animationNodes[parentTag];
RCTAnimatedNode *childNode = _animationNodes[childTag];
RCTAssertParam(parentNode);
RCTAssertParam(childNode);
[parentNode removeChild:childNode];
}
RCT_EXPORT_METHOD(startAnimatingNode:(nonnull NSNumber *)animationId
nodeTag:(nonnull NSNumber *)nodeTag
config:(NSDictionary<NSString *, id> *)config
endCallback:(RCTResponseSenderBlock)callBack)
{
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)_animationNodes[nodeTag];
NSString *type = config[@"type"];
id<RCTAnimationDriver> animationDriver;
if ([type isEqual:@"frames"]) {
animationDriver = [[RCTFrameAnimation alloc] initWithId:animationId
config:config
forNode:valueNode
callBack:callBack];
} else if ([type isEqual:@"spring"]) {
animationDriver = [[RCTSpringAnimation alloc] initWithId:animationId
config:config
forNode:valueNode
callBack:callBack];
} else {
RCTLogError(@"Unsupported animation type: %@", config[@"type"]);
return;
}
[_activeAnimations addObject:animationDriver];
_animationDrivers[animationId] = animationDriver;
[animationDriver startAnimation];
[self startAnimationLoopIfNeeded];
}
RCT_EXPORT_METHOD(stopAnimation:(nonnull NSNumber *)animationId)
{
id<RCTAnimationDriver> driver = _animationDrivers[animationId];
if (driver) {
[driver removeAnimation];
[_animationDrivers removeObjectForKey:animationId];
[_activeAnimations removeObject:driver];
[_finishedAnimations removeObject:driver];
}
}
RCT_EXPORT_METHOD(setAnimatedNodeValue:(nonnull NSNumber *)nodeTag
value:(nonnull NSNumber *)value)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
valueNode.value = value.floatValue;
[valueNode setNeedsUpdate];
[self updateViewsProps];
[valueNode cleanupAnimationUpdate];
}
RCT_EXPORT_METHOD(setAnimatedNodeOffset:(nonnull NSNumber *)nodeTag
offset:(nonnull NSNumber *)offset)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
[valueNode setOffset:offset.floatValue];
[_updatedValueNodes addObject:valueNode];
}
RCT_EXPORT_METHOD(flattenAnimatedNodeOffset:(nonnull NSNumber *)nodeTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
[valueNode flattenOffset];
}
RCT_EXPORT_METHOD(extractAnimatedNodeOffset:(nonnull NSNumber *)nodeTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Not a value node.");
return;
}
RCTValueAnimatedNode *valueNode = (RCTValueAnimatedNode *)node;
[valueNode extractOffset];
}
RCT_EXPORT_METHOD(connectAnimatedNodeToView:(nonnull NSNumber *)nodeTag
viewTag:(nonnull NSNumber *)viewTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (viewTag && [node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[(RCTPropsAnimatedNode *)node connectToView:viewTag animatedModule:self];
}
}
RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag
viewTag:(nonnull NSNumber *)viewTag)
{
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (viewTag && node && [node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[(RCTPropsAnimatedNode *)node disconnectFromView:viewTag];
}
}
RCT_EXPORT_METHOD(dropAnimatedNode:(nonnull NSNumber *)tag)
{
RCTAnimatedNode *node = _animationNodes[tag];
if (node) {
[node detachNode];
[_animationNodes removeObjectForKey:tag];
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) {
[_updatedValueNodes removeObject:(RCTValueAnimatedNode *)node];
} else if ([node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[_propAnimationNodes removeObject:(RCTPropsAnimatedNode *)node];
}
}
}
RCT_EXPORT_METHOD(startListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
{
RCTAnimatedNode *node = _animationNodes[tag];
if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) {
((RCTValueAnimatedNode *)node).valueObserver = self;
}
}
RCT_EXPORT_METHOD(stopListeningToAnimatedNodeValue:(nonnull NSNumber *)tag)
{
RCTAnimatedNode *node = _animationNodes[tag];
if (node && [node isKindOfClass:[RCTValueAnimatedNode class]]) {
((RCTValueAnimatedNode *)node).valueObserver = nil;
}
}
RCT_EXPORT_METHOD(addAnimatedEventToView:(nonnull NSNumber *)viewTag
eventName:(nonnull NSString *)eventName
eventMapping:(NSDictionary<NSString *, id> *)eventMapping)
{
NSNumber *nodeTag = [RCTConvert NSNumber:eventMapping[@"animatedValueTag"]];
RCTAnimatedNode *node = _animationNodes[nodeTag];
if (!node) {
RCTLogError(@"Animated node with tag %@ does not exists", nodeTag);
return;
}
if (![node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTLogError(@"Animated node connected to event should be of type RCTValueAnimatedNode");
return;
}
NSArray<NSString *> *eventPath = [RCTConvert NSStringArray:eventMapping[@"nativeEventPath"]];
RCTEventAnimation *driver =
[[RCTEventAnimation alloc] initWithEventPath:eventPath valueNode:(RCTValueAnimatedNode *)node];
_eventAnimationDrivers[[NSString stringWithFormat:@"%@%@", viewTag, eventName]] = driver;
}
RCT_EXPORT_METHOD(removeAnimatedEventFromView:(nonnull NSNumber *)viewTag
eventName:(nonnull NSString *)eventName)
{
[_eventAnimationDrivers removeObjectForKey:[NSString stringWithFormat:@"%@%@", viewTag, eventName]];
}
- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value
{
[self sendEventWithName:@"onAnimatedValueUpdate"
body:@{@"tag": node.nodeTag, @"value": @(value)}];
}
- (void)eventDispatcherWillDispatchEvent:(id<RCTEvent>)event
{
// Native animated events only work for events dispatched from the main queue.
if (!RCTIsMainQueue() || _eventAnimationDrivers.count == 0) {
return;
}
NSString *key = [NSString stringWithFormat:@"%@%@", event.viewTag, event.eventName];
RCTEventAnimation *driver = _eventAnimationDrivers[key];
if (driver) {
[driver updateWithEvent:event];
[self updateViewsProps];
[driver.valueNode cleanupAnimationUpdate];
}
}
- (void)updateViewsProps
{
for (RCTPropsAnimatedNode *propsNode in _propAnimationNodes) {
[propsNode updateNodeIfNecessary];
}
}
#pragma mark -- Animation Loop
- (void)startAnimationLoopIfNeeded
{
if (!_displayLink && (_activeAnimations.count > 0 || _updatedValueNodes.count > 0)) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateAnimations)];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
}
- (void)stopAnimationLoopIfNeeded
{
if (_displayLink && _activeAnimations.count == 0 && _updatedValueNodes.count == 0) {
[_displayLink invalidate];
_displayLink = nil;
}
}
- (void)updateAnimations
{
// Step Current active animations
// This also recursively marks children nodes as needing update
for (id<RCTAnimationDriver> animationDriver in _activeAnimations) {
[animationDriver stepAnimation];
}
// Perform node updates for marked nodes.
// At this point all nodes that are in need of an update are properly marked as such.
for (RCTPropsAnimatedNode *propsNode in _propAnimationNodes) {
[propsNode updateNodeIfNecessary];
}
// Cleanup nodes and prepare for next cycle. Remove updated nodes from bucket.
for (id<RCTAnimationDriver> driverNode in _activeAnimations) {
[driverNode cleanupAnimationUpdate];
}
for (RCTValueAnimatedNode *valueNode in _updatedValueNodes) {
[valueNode cleanupAnimationUpdate];
}
[_updatedValueNodes removeAllObjects];
for (id<RCTAnimationDriver> driverNode in _activeAnimations) {
if (driverNode.animationHasFinished) {
[driverNode removeAnimation];
[_finishedAnimations addObject:driverNode];
}
}
for (id<RCTAnimationDriver> driverNode in _finishedAnimations) {
[_activeAnimations removeObject:driverNode];
[_animationDrivers removeObjectForKey:driverNode.animationId];
}
[_finishedAnimations removeAllObjects];
[self stopAnimationLoopIfNeeded];
}
@end