Add support for value listener
Summary: Adds support for `Animated.Value#addListener` for native driven animated values. Same as #8844 but for iOS. This depends on some JS code in #8844 so only review the 2nd commit and let's wait for #8844 to land first. **Test plan** Tested using the UIExplorer example. Closes https://github.com/facebook/react-native/pull/9194 Differential Revision: D3681749 fbshipit-source-id: 521a61e2221c1ad1f6f40c75dd2dc957361d0271
This commit is contained in:
parent
68d483e041
commit
0e204e1141
|
@ -347,7 +347,6 @@ exports.examples = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Animated value listener',
|
title: 'Animated value listener',
|
||||||
platform: 'android',
|
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<ValueListenerExample />
|
<ValueListenerExample />
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var DeviceEventEmitter = require('RCTDeviceEventEmitter');
|
|
||||||
var InteractionManager = require('InteractionManager');
|
var InteractionManager = require('InteractionManager');
|
||||||
var Interpolation = require('Interpolation');
|
var Interpolation = require('Interpolation');
|
||||||
var React = require('React');
|
var React = require('React');
|
||||||
|
@ -750,13 +749,12 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||||
}
|
}
|
||||||
|
|
||||||
_startListeningToNativeValueUpdates() {
|
_startListeningToNativeValueUpdates() {
|
||||||
if (this.__nativeAnimatedValueListener ||
|
if (this.__nativeAnimatedValueListener) {
|
||||||
!NativeAnimatedHelper.supportsNativeListener()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
|
NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag());
|
||||||
this.__nativeAnimatedValueListener = DeviceEventEmitter.addListener(
|
this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener(
|
||||||
'onAnimatedValueUpdate',
|
'onAnimatedValueUpdate',
|
||||||
(data) => {
|
(data) => {
|
||||||
if (data.tag !== this.__getNativeTag()) {
|
if (data.tag !== this.__getNativeTag()) {
|
||||||
|
@ -768,8 +766,7 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||||
}
|
}
|
||||||
|
|
||||||
_stopListeningForNativeValueUpdates() {
|
_stopListeningForNativeValueUpdates() {
|
||||||
if (!this.__nativeAnimatedValueListener ||
|
if (!this.__nativeAnimatedValueListener) {
|
||||||
!NativeAnimatedHelper.supportsNativeListener()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,21 +11,24 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'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 */
|
let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */
|
||||||
var __nativeAnimationIdCount = 1; /* used for started animations */
|
let __nativeAnimationIdCount = 1; /* used for started animations */
|
||||||
|
|
||||||
type EndResult = {finished: bool};
|
type EndResult = {finished: bool};
|
||||||
type EndCallback = (result: EndResult) => void;
|
type EndCallback = (result: EndResult) => void;
|
||||||
|
|
||||||
|
let nativeEventEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* the native module methods
|
||||||
*/
|
*/
|
||||||
var API = {
|
const API = {
|
||||||
createAnimatedNode: function(tag: number, config: Object): void {
|
createAnimatedNode: function(tag: number, config: Object): void {
|
||||||
assertNativeAnimatedModule();
|
assertNativeAnimatedModule();
|
||||||
NativeAnimatedModule.createAnimatedNode(tag, config);
|
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 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.
|
* to the properties that will perform best when animated off the JS thread.
|
||||||
*/
|
*/
|
||||||
var PROPS_WHITELIST = {
|
const PROPS_WHITELIST = {
|
||||||
style: {
|
style: {
|
||||||
opacity: true,
|
opacity: true,
|
||||||
transform: true,
|
transform: true,
|
||||||
|
@ -91,7 +94,7 @@ var PROPS_WHITELIST = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var TRANSFORM_WHITELIST = {
|
const TRANSFORM_WHITELIST = {
|
||||||
translateX: true,
|
translateX: true,
|
||||||
translateY: true,
|
translateY: true,
|
||||||
scale: true,
|
scale: true,
|
||||||
|
@ -152,11 +155,6 @@ function assertNativeAnimatedModule(): void {
|
||||||
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove this when iOS supports native listeners.
|
|
||||||
function supportsNativeListener(): bool {
|
|
||||||
return !!NativeAnimatedModule.startListeningToAnimatedNodeValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
API,
|
API,
|
||||||
validateProps,
|
validateProps,
|
||||||
|
@ -166,5 +164,10 @@ module.exports = {
|
||||||
generateNewNodeTag,
|
generateNewNodeTag,
|
||||||
generateNewAnimationId,
|
generateNewAnimationId,
|
||||||
assertNativeAnimatedModule,
|
assertNativeAnimatedModule,
|
||||||
supportsNativeListener,
|
get nativeEventEmitter() {
|
||||||
|
if (!nativeEventEmitter) {
|
||||||
|
nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule);
|
||||||
|
}
|
||||||
|
return nativeEventEmitter;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,8 +10,17 @@
|
||||||
#import "RCTAnimatedNode.h"
|
#import "RCTAnimatedNode.h"
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@class RCTValueAnimatedNode;
|
||||||
|
|
||||||
|
@protocol RCTValueAnimatedNodeObserver <NSObject>
|
||||||
|
|
||||||
|
- (void)animatedNode:(RCTValueAnimatedNode *)node didUpdateValue:(CGFloat)value;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@interface RCTValueAnimatedNode : RCTAnimatedNode
|
@interface RCTValueAnimatedNode : RCTAnimatedNode
|
||||||
|
|
||||||
@property (nonatomic, assign) CGFloat value;
|
@property (nonatomic, assign) CGFloat value;
|
||||||
|
@property (nonatomic, weak) id<RCTValueAnimatedNodeObserver> valueObserver;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -11,4 +11,13 @@
|
||||||
|
|
||||||
@implementation RCTValueAnimatedNode
|
@implementation RCTValueAnimatedNode
|
||||||
|
|
||||||
|
- (void)setValue:(CGFloat)value
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
|
||||||
|
if (_valueObserver) {
|
||||||
|
[_valueObserver animatedNode:self didUpdateValue:_value];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
*/
|
*/
|
||||||
#import "RCTBridgeModule.h"
|
#import "RCTBridgeModule.h"
|
||||||
|
#import "RCTValueAnimatedNode.h"
|
||||||
|
#import "RCTEventEmitter.h"
|
||||||
|
|
||||||
@interface RCTNativeAnimatedModule : NSObject <RCTBridgeModule>
|
@interface RCTNativeAnimatedModule : RCTEventEmitter <RCTBridgeModule, RCTValueAnimatedNodeObserver>
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -32,13 +32,12 @@
|
||||||
CADisplayLink *_displayLink;
|
CADisplayLink *_displayLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (void)setBridge:(RCTBridge *)bridge
|
- (void)setBridge:(RCTBridge *)bridge
|
||||||
{
|
{
|
||||||
_bridge = bridge;
|
[super setBridge:bridge];
|
||||||
|
|
||||||
_animationNodes = [NSMutableDictionary new];
|
_animationNodes = [NSMutableDictionary new];
|
||||||
_animationDrivers = [NSMutableDictionary new];
|
_animationDrivers = [NSMutableDictionary new];
|
||||||
_activeAnimations = [NSMutableSet new];
|
_activeAnimations = [NSMutableSet new];
|
||||||
|
@ -47,11 +46,17 @@ RCT_EXPORT_MODULE()
|
||||||
_propAnimationNodes = [NSMutableSet new];
|
_propAnimationNodes = [NSMutableSet new];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (dispatch_queue_t)methodQueue
|
- (dispatch_queue_t)methodQueue
|
||||||
{
|
{
|
||||||
return dispatch_get_main_queue();
|
return dispatch_get_main_queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)supportedEvents
|
||||||
|
{
|
||||||
|
return @[@"onAnimatedValueUpdate"];
|
||||||
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
|
RCT_EXPORT_METHOD(createAnimatedNode:(nonnull NSNumber *)tag
|
||||||
config:(NSDictionary<NSString *, id> *)config)
|
config:(NSDictionary<NSString *, id> *)config)
|
||||||
{
|
{
|
||||||
|
@ -198,6 +203,29 @@ RCT_EXPORT_METHOD(dropAnimatedNode:(nonnull NSNumber *)tag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
#pragma mark -- Animation Loop
|
||||||
|
|
||||||
- (void)startAnimation
|
- (void)startAnimation
|
||||||
|
|
Loading…
Reference in New Issue