diff --git a/Examples/UIExplorer/js/NativeAnimationsProblemExample.js b/Examples/UIExplorer/js/NativeAnimationsProblemExample.js
new file mode 100644
index 00000000000000..5cdf0977cdc17d
--- /dev/null
+++ b/Examples/UIExplorer/js/NativeAnimationsProblemExample.js
@@ -0,0 +1,120 @@
+/**
+ * Copyright (c) 2013-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.
+ *
+ * The examples provided by Facebook are for non-commercial testing and
+ * evaluation purposes only.
+ *
+ * Facebook reserves all rights not expressly granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
+ * FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * @flow
+ */
+'use strict';
+
+var React = require('react');
+var ReactNative = require('react-native');
+var {
+ View,
+ Text,
+ Animated,
+ StyleSheet,
+ TouchableWithoutFeedback,
+} = ReactNative;
+
+class Tester extends React.Component {
+
+ static title = 'Native Animated Problem';
+ static description = 'Test out Native Animations';
+
+ state = {
+ opacity: new Animated.Value(0.25, {useNativeDriver: true}),
+ showBlock2: false,
+ };
+
+ visible = false;
+
+ runAnimation = () => {
+ this.visible = !this.visible;
+ Animated.timing(this.state.opacity, {
+ toValue: this.visible ? 1 : 0.25,
+ duration: 500,
+ useNativeDriver: true,
+ }).start();
+ };
+
+ render() {
+ return (
+
+
+
+
+ {'Run animation'}
+
+
+
+
+ {
+ this.setState({
+ showBlock2: !this.state.showBlock2,
+ });
+ }}>
+
+ {'Toggle block 2'}
+
+
+
+
+ {this._renderBlock(1)}
+
+
+ {this.state.showBlock2 && this._renderBlock(2)}
+
+
+ );
+ }
+
+ _renderBlock(key: number) {
+ return (
+
+ );
+ }
+}
+
+const styles = StyleSheet.create({
+ container: {
+ padding: 10,
+ },
+ button: {
+ padding: 10,
+ backgroundColor: 'green',
+ },
+ section: {
+ marginBottom: 20,
+ },
+ block: {
+ width: 50,
+ height: 50,
+ backgroundColor: 'blue',
+ },
+});
+
+module.exports = Tester;
diff --git a/Examples/UIExplorer/js/UIExplorerList.android.js b/Examples/UIExplorer/js/UIExplorerList.android.js
index de1f23b35d19f9..818e26deeca18e 100644
--- a/Examples/UIExplorer/js/UIExplorerList.android.js
+++ b/Examples/UIExplorer/js/UIExplorerList.android.js
@@ -179,6 +179,10 @@ const APIExamples: Array = [
key: 'NativeAnimationsExample',
module: require('./NativeAnimationsExample'),
},
+ {
+ key: 'NativeAnimationsProblemExample',
+ module: require('./NativeAnimationsProblemExample'),
+ },
{
key: 'NavigationExperimentalExample',
module: require('./NavigationExperimental/NavigationExperimentalExample'),
diff --git a/Examples/UIExplorer/js/UIExplorerList.ios.js b/Examples/UIExplorer/js/UIExplorerList.ios.js
index 71fc56312a220e..951b269e3bd4ac 100644
--- a/Examples/UIExplorer/js/UIExplorerList.ios.js
+++ b/Examples/UIExplorer/js/UIExplorerList.ios.js
@@ -231,6 +231,10 @@ const APIExamples: Array = [
key: 'NativeAnimationsExample',
module: require('./NativeAnimationsExample'),
},
+ {
+ key: 'NativeAnimationsProblemExample',
+ module: require('./NativeAnimationsProblemExample'),
+ },
{
key: 'NavigationExperimentalExample',
module: require('./NavigationExperimental/NavigationExperimentalExample'),
diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js
index 7fb99069c559b8..424e137ecffd3e 100644
--- a/Libraries/Animated/src/AnimatedImplementation.js
+++ b/Libraries/Animated/src/AnimatedImplementation.js
@@ -659,6 +659,10 @@ class SpringAnimation extends Animation {
}
}
+type AnimatedValueConfig = {
+ useNativeDriver?: bool;
+};
+
type ValueListenerCallback = (state: {value: number}) => void;
var _uniqueId = 1;
@@ -678,12 +682,15 @@ class AnimatedValue extends AnimatedWithChildren {
_listeners: {[key: string]: ValueListenerCallback};
__nativeAnimatedValueListener: ?any;
- constructor(value: number) {
+ constructor(value: number, config?: AnimatedValueConfig) {
super();
this._startingValue = this._value = value;
this._offset = 0;
this._animation = null;
this._listeners = {};
+ if (config && config.useNativeDriver) {
+ this.__makeNative();
+ }
}
__detach() {
@@ -929,7 +936,7 @@ class AnimatedValueXY extends AnimatedWithChildren {
y: AnimatedValue;
_listeners: {[key: string]: {x: string, y: string}};
- constructor(valueIn?: ?{x: number | AnimatedValue, y: number | AnimatedValue}) {
+ constructor(valueIn?: ?{x: number | AnimatedValue; y: number | AnimatedValue}, config?: AnimatedValueConfig) {
super();
var value: any = valueIn || {x: 0, y: 0}; // @flowfixme: shouldn't need `: any`
if (typeof value.x === 'number' && typeof value.y === 'number') {
@@ -946,6 +953,9 @@ class AnimatedValueXY extends AnimatedWithChildren {
this.y = value.y;
}
this._listeners = {};
+ if (config && config.useNativeDriver) {
+ this.__makeNative();
+ }
}
setValue(value: {x: number, y: number}) {
diff --git a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h
index 8e06e90629fdd6..07e5a85555306e 100644
--- a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h
+++ b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h
@@ -21,7 +21,6 @@
@property (nonatomic, copy, readonly) NSDictionary *parentNodes;
@property (nonatomic, readonly) BOOL needsUpdate;
-@property (nonatomic, readonly) BOOL hasUpdated;
/**
* Marks a node and its children as needing update.
diff --git a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m
index 9f5075b6222140..0a1c35fa77351c 100644
--- a/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m
+++ b/Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m
@@ -69,6 +69,7 @@ - (void)onAttachedToNode:(RCTAnimatedNode *)parent
if (parent) {
_parentNodes[parent.nodeTag] = parent;
}
+ [self setNeedsUpdate];
}
- (void)onDetachedFromNode:(RCTAnimatedNode *)parent
@@ -79,6 +80,7 @@ - (void)onDetachedFromNode:(RCTAnimatedNode *)parent
if (parent) {
[_parentNodes removeObjectForKey:parent.nodeTag];
}
+ [self setNeedsUpdate];
}
- (void)detachNode
@@ -93,10 +95,6 @@ - (void)detachNode
- (void)setNeedsUpdate
{
- if (_needsUpdate) {
- // Has already been marked. Stop branch.
- return;
- }
_needsUpdate = YES;
for (RCTAnimatedNode *child in _childNodes.allValues) {
[child setNeedsUpdate];
@@ -105,9 +103,7 @@ - (void)setNeedsUpdate
- (void)cleanupAnimationUpdate
{
- if (_hasUpdated) {
- _needsUpdate = NO;
- _hasUpdated = NO;
+ if (!_needsUpdate) {
for (RCTAnimatedNode *child in _childNodes.allValues) {
[child cleanupAnimationUpdate];
}
@@ -116,7 +112,7 @@ - (void)cleanupAnimationUpdate
- (void)updateNodeIfNecessary
{
- if (_needsUpdate && !_hasUpdated) {
+ if (_needsUpdate) {
for (RCTAnimatedNode *parent in _parentNodes.allValues) {
[parent updateNodeIfNecessary];
}
@@ -126,7 +122,7 @@ - (void)updateNodeIfNecessary
- (void)performUpdate
{
- _hasUpdated = YES;
+ _needsUpdate = NO;
// To be overidden by subclasses
// This method is called on a node only if it has been marked for update
// during the current update loop
diff --git a/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m
index 2c349bd22e81d7..feb7d1ad7f5acf 100644
--- a/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m
+++ b/Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m
@@ -38,7 +38,7 @@ - (void)performUpdate
NSDictionary *style = self.config[@"style"];
[style enumerateKeysAndObjectsUsingBlock:^(NSString *property, NSNumber *nodeTag, __unused BOOL *stop) {
RCTAnimatedNode *node = self.parentNodes[nodeTag];
- if (node && node.hasUpdated) {
+ if (node) {
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node;
[self->_updatedPropsDictionary setObject:@(parentNode.value) forKey:property];
diff --git a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m
index d44fe4d7c356fe..778413e0725fc2 100644
--- a/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m
+++ b/Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m
@@ -46,7 +46,7 @@ - (void)performUpdate
NSNumber *nodeTag = transformConfig[@"nodeTag"];
RCTAnimatedNode *node = self.parentNodes[nodeTag];
- if (node.hasUpdated && [node isKindOfClass:[RCTValueAnimatedNode class]]) {
+ if ([node isKindOfClass:[RCTValueAnimatedNode class]]) {
RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node;
NSString *property = transformConfig[@"property"];
diff --git a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
index 29d2a6c14a7a7c..b79a0857f76880 100644
--- a/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
+++ b/Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m
@@ -27,6 +27,7 @@ - (instancetype)initWithTag:(NSNumber *)tag
_value = [self.config[@"value"] floatValue];
}
return self;
+
}
- (void)flattenOffset
diff --git a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
index 5c76c10e4623bd..8b16bfe232ecfd 100644
--- a/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
+++ b/Libraries/NativeAnimation/RCTNativeAnimatedModule.m
@@ -51,7 +51,6 @@ - (void)setBridge:(RCTBridge *)bridge
_propAnimationNodes = [NSMutableSet new];
}
-
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
@@ -218,6 +217,8 @@ - (dispatch_queue_t)methodQueue
if (viewTag && [node isKindOfClass:[RCTPropsAnimatedNode class]]) {
[(RCTPropsAnimatedNode *)node connectToView:viewTag animatedModule:self];
}
+ [node setNeedsUpdate];
+ [node updateNodeIfNecessary];
}
RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag