diff --git a/Examples/UIExplorer/js/NativeAnimationsExample.js b/Examples/UIExplorer/js/NativeAnimationsExample.js
index bd7add201d258d..0c26a46fd3b580 100644
--- a/Examples/UIExplorer/js/NativeAnimationsExample.js
+++ b/Examples/UIExplorer/js/NativeAnimationsExample.js
@@ -347,7 +347,6 @@ exports.examples = [
},
{
title: 'Animated value listener',
- platform: 'android',
render: function() {
return (
diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js
index 3d78bfed4484c2..d7403ef638bae7 100644
--- a/Libraries/Animated/src/AnimatedImplementation.js
+++ b/Libraries/Animated/src/AnimatedImplementation.js
@@ -11,7 +11,6 @@
*/
'use strict';
-var DeviceEventEmitter = require('RCTDeviceEventEmitter');
var InteractionManager = require('InteractionManager');
var Interpolation = require('Interpolation');
var React = require('React');
@@ -724,23 +723,24 @@ class AnimatedValue extends AnimatedWithChildren {
}
_startListeningToNativeValueUpdates() {
- if (this.__nativeAnimatedValueListener ||
- !NativeAnimatedHelper.supportsNativeListener()) {
+ if (this.__nativeAnimatedValueListener) {
return;
}
NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
- this.__nativeAnimatedValueListener = DeviceEventEmitter.addListener('onAnimatedValueUpdate', (data) => {
- if (data.tag !== this.__getNativeTag()) {
- return;
+ this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener(
+ 'onAnimatedValueUpdate',
+ (data) => {
+ if (data.tag !== this.__getNativeTag()) {
+ return;
+ }
+ this._updateValue(data.value, false /* flush */);
}
- this._updateValue(data.value, false /* flush */);
- });
+ );
}
_stopListeningForNativeValueUpdates() {
- if (!this.__nativeAnimatedValueListener ||
- !NativeAnimatedHelper.supportsNativeListener()) {
+ if (!this.__nativeAnimatedValueListener) {
return;
}
diff --git a/Libraries/Animated/src/NativeAnimatedHelper.js b/Libraries/Animated/src/NativeAnimatedHelper.js
index 9138a4308e351b..769b2228d69091 100644
--- a/Libraries/Animated/src/NativeAnimatedHelper.js
+++ b/Libraries/Animated/src/NativeAnimatedHelper.js
@@ -11,21 +11,24 @@
*/
'use strict';
-var NativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
+const NativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
+const NativeEventEmitter = require('NativeEventEmitter');
-var invariant = require('fbjs/lib/invariant');
+const invariant = require('fbjs/lib/invariant');
-var __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
-var __nativeAnimationIdCount = 1; /* used for started animations */
+let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
+let __nativeAnimationIdCount = 1; /* used for started animations */
type EndResult = {finished: bool};
type EndCallback = (result: EndResult) => void;
+const nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule);
+
/**
- * Simple wrappers around NativeANimatedModule to provide flow and autocmplete support for
+ * Simple wrappers around NativeAnimatedModule to provide flow and autocmplete support for
* the native module methods
*/
-var API = {
+const API = {
createAnimatedNode: function(tag: number, config: Object): void {
assertNativeAnimatedModule();
NativeAnimatedModule.createAnimatedNode(tag, config);
@@ -79,7 +82,7 @@ var API = {
* to be updated through the shadow view hierarchy (all non-layout properties). This list is limited
* to the properties that will perform best when animated off the JS thread.
*/
-var PROPS_WHITELIST = {
+const PROPS_WHITELIST = {
style: {
opacity: true,
transform: true,
@@ -91,7 +94,7 @@ var PROPS_WHITELIST = {
},
};
-var TRANSFORM_WHITELIST = {
+const TRANSFORM_WHITELIST = {
translateX: true,
translateY: true,
scale: true,
@@ -152,11 +155,6 @@ function assertNativeAnimatedModule(): void {
invariant(NativeAnimatedModule, 'Native animated module is not available');
}
-// TODO: remove this when iOS supports native listeners.
-function supportsNativeListener(): bool {
- return !!NativeAnimatedModule.startListeningToAnimatedNodeValue;
-}
-
module.exports = {
API,
validateProps,
@@ -166,5 +164,5 @@ module.exports = {
generateNewNodeTag,
generateNewAnimationId,
assertNativeAnimatedModule,
- supportsNativeListener,
+ nativeEventEmitter,
};
diff --git a/Libraries/Animated/src/__tests__/Animated-test.js b/Libraries/Animated/src/__tests__/Animated-test.js
index 02cb505755dab4..d2ea92f3fa9d4c 100644
--- a/Libraries/Animated/src/__tests__/Animated-test.js
+++ b/Libraries/Animated/src/__tests__/Animated-test.js
@@ -13,7 +13,8 @@ jest
.setMock('Text', {})
.setMock('View', {})
.setMock('Image', {})
- .setMock('React', {Component: class {}});
+ .setMock('React', {Component: class {}})
+ .setMock('NativeEventEmitter', class {});
var Animated = require('Animated');
diff --git a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h
index af183243534cdd..5029129ec27db8 100644
--- a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h
+++ b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.h
@@ -10,8 +10,17 @@
#import "RCTAnimatedNode.h"
#import
+@class RCTValueAnimatedNode;
+
+@protocol RCTValueAnimatedNodeObserver
+
+- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value;
+
+@end
+
@interface RCTValueAnimatedNode : RCTAnimatedNode
@property (nonatomic, assign) CGFloat value;
+@property (nonatomic, weak) id valueObserver;
@end
diff --git a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
index cffa66681d7b17..40ca5b9eafdabd 100644
--- a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
+++ b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
@@ -11,4 +11,13 @@
@implementation RCTValueAnimatedNode
+- (void)setValue:(CGFloat)value
+{
+ _value = value;
+
+ if (_valueObserver) {
+ [_valueObserver animatedNode:self didUpdateValue:_value];
+ }
+}
+
@end
diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h
index d099ac53bfacf9..da7831769194f6 100644
--- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.h
+++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.h
@@ -7,7 +7,9 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTBridgeModule.h"
+#import "RCTValueAnimatedNode.h"
+#import "RCTEventEmitter.h"
-@interface RCTNativeAnimatedModule : NSObject
+@interface RCTNativeAnimatedModule : RCTEventEmitter
@end
diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
index 74e157fc17fb6b..a8694460c3bb25 100644
--- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
+++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
@@ -32,13 +32,12 @@ @implementation RCTNativeAnimatedModule
CADisplayLink *_displayLink;
}
-@synthesize bridge = _bridge;
-
RCT_EXPORT_MODULE()
- (void)setBridge:(RCTBridge *)bridge
{
- _bridge = bridge;
+ [super setBridge:bridge];
+
_animationNodes = [NSMutableDictionary new];
_animationDrivers = [NSMutableDictionary new];
_activeAnimations = [NSMutableSet new];
@@ -47,6 +46,11 @@ - (void)setBridge:(RCTBridge *)bridge
_propAnimationNodes = [NSMutableSet new];
}
+- (NSArray *)supportedEvents
+{
+ return @[@"onAnimatedValueUpdate"];
+}
+
RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
config:(NSDictionary *)config)
{
@@ -193,6 +197,29 @@ - (void)setBridge:(RCTBridge *)bridge
}
}
+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;
+ }
+}
+
+- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value
+{
+ [self sendEventWithName:@"onAnimatedValueUpdate"
+ body:@{@"tag": node.nodeTag, @"value": @(value)}];
+}
+
+
#pragma mark -- Animation Loop
- (void)startAnimation