Skip to content

Commit e5572d0

Browse files
committed
Native animation problems & solutions
1 parent ed0e8f3 commit e5572d0

File tree

10 files changed

+150
-15
lines changed

10 files changed

+150
-15
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* Copyright (c) 2013-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* The examples provided by Facebook are for non-commercial testing and
10+
* evaluation purposes only.
11+
*
12+
* Facebook reserves all rights not expressly granted.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
17+
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18+
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*
21+
* @flow
22+
*/
23+
'use strict';
24+
25+
var React = require('react');
26+
var ReactNative = require('react-native');
27+
var {
28+
View,
29+
Text,
30+
Animated,
31+
StyleSheet,
32+
TouchableWithoutFeedback,
33+
} = ReactNative;
34+
35+
class Tester extends React.Component {
36+
37+
static title = 'Native Animated Problem';
38+
static description = 'Test out Native Animations';
39+
40+
state = {
41+
opacity: new Animated.Value(0.25, {useNativeDriver: true}),
42+
showBlock2: false,
43+
};
44+
45+
visible = false;
46+
47+
runAnimation = () => {
48+
this.visible = !this.visible;
49+
Animated.timing(this.state.opacity, {
50+
toValue: this.visible ? 1 : 0.25,
51+
duration: 500,
52+
useNativeDriver: true,
53+
}).start();
54+
};
55+
56+
render() {
57+
return (
58+
<View style={styles.container}>
59+
<View style={styles.section}>
60+
<TouchableWithoutFeedback onPress={this.runAnimation}>
61+
<View style={styles.button}>
62+
<Text>{'Run animation'}</Text>
63+
</View>
64+
</TouchableWithoutFeedback>
65+
</View>
66+
<View style={styles.section}>
67+
<TouchableWithoutFeedback onPress={() => {
68+
this.setState({
69+
showBlock2: !this.state.showBlock2,
70+
});
71+
}}>
72+
<View style={styles.button}>
73+
<Text>{'Toggle block 2'}</Text>
74+
</View>
75+
</TouchableWithoutFeedback>
76+
</View>
77+
<View style={styles.section}>
78+
{this._renderBlock(1)}
79+
</View>
80+
<View style={styles.section}>
81+
{this.state.showBlock2 && this._renderBlock(2)}
82+
</View>
83+
</View>
84+
);
85+
}
86+
87+
_renderBlock(key: number) {
88+
return (
89+
<Animated.View
90+
key={key}
91+
style={[
92+
styles.block,
93+
{
94+
opacity: this.state.opacity,
95+
}
96+
]}
97+
/>
98+
);
99+
}
100+
}
101+
102+
const styles = StyleSheet.create({
103+
container: {
104+
padding: 10,
105+
},
106+
button: {
107+
padding: 10,
108+
backgroundColor: 'green',
109+
},
110+
section: {
111+
marginBottom: 20,
112+
},
113+
block: {
114+
width: 50,
115+
height: 50,
116+
backgroundColor: 'blue',
117+
},
118+
});
119+
120+
module.exports = Tester;

Examples/UIExplorer/js/UIExplorerList.android.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ const APIExamples: Array<UIExplorerExample> = [
179179
key: 'NativeAnimationsExample',
180180
module: require('./NativeAnimationsExample'),
181181
},
182+
{
183+
key: 'NativeAnimationsProblemExample',
184+
module: require('./NativeAnimationsProblemExample'),
185+
},
182186
{
183187
key: 'NavigationExperimentalExample',
184188
module: require('./NavigationExperimental/NavigationExperimentalExample'),

Examples/UIExplorer/js/UIExplorerList.ios.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ const APIExamples: Array<UIExplorerExample> = [
231231
key: 'NativeAnimationsExample',
232232
module: require('./NativeAnimationsExample'),
233233
},
234+
{
235+
key: 'NativeAnimationsProblemExample',
236+
module: require('./NativeAnimationsProblemExample'),
237+
},
234238
{
235239
key: 'NavigationExperimentalExample',
236240
module: require('./NavigationExperimental/NavigationExperimentalExample'),

Libraries/Animated/src/AnimatedImplementation.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,10 @@ class SpringAnimation extends Animation {
659659
}
660660
}
661661

662+
type AnimatedValueConfig = {
663+
useNativeDriver?: bool;
664+
};
665+
662666
type ValueListenerCallback = (state: {value: number}) => void;
663667

664668
var _uniqueId = 1;
@@ -678,12 +682,15 @@ class AnimatedValue extends AnimatedWithChildren {
678682
_listeners: {[key: string]: ValueListenerCallback};
679683
__nativeAnimatedValueListener: ?any;
680684

681-
constructor(value: number) {
685+
constructor(value: number, config?: AnimatedValueConfig) {
682686
super();
683687
this._startingValue = this._value = value;
684688
this._offset = 0;
685689
this._animation = null;
686690
this._listeners = {};
691+
if (config && config.useNativeDriver) {
692+
this.__makeNative();
693+
}
687694
}
688695

689696
__detach() {
@@ -929,7 +936,7 @@ class AnimatedValueXY extends AnimatedWithChildren {
929936
y: AnimatedValue;
930937
_listeners: {[key: string]: {x: string, y: string}};
931938

932-
constructor(valueIn?: ?{x: number | AnimatedValue, y: number | AnimatedValue}) {
939+
constructor(valueIn?: ?{x: number | AnimatedValue; y: number | AnimatedValue}, config?: AnimatedValueConfig) {
933940
super();
934941
var value: any = valueIn || {x: 0, y: 0}; // @flowfixme: shouldn't need `: any`
935942
if (typeof value.x === 'number' && typeof value.y === 'number') {
@@ -946,6 +953,9 @@ class AnimatedValueXY extends AnimatedWithChildren {
946953
this.y = value.y;
947954
}
948955
this._listeners = {};
956+
if (config && config.useNativeDriver) {
957+
this.__makeNative();
958+
}
949959
}
950960

951961
setValue(value: {x: number, y: number}) {

Libraries/NativeAnimation/Nodes/RCTAnimatedNode.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
@property (nonatomic, copy, readonly) NSDictionary<NSNumber *, RCTAnimatedNode *> *parentNodes;
2222

2323
@property (nonatomic, readonly) BOOL needsUpdate;
24-
@property (nonatomic, readonly) BOOL hasUpdated;
2524

2625
/**
2726
* Marks a node and its children as needing update.

Libraries/NativeAnimation/Nodes/RCTAnimatedNode.m

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ - (void)onAttachedToNode:(RCTAnimatedNode *)parent
6969
if (parent) {
7070
_parentNodes[parent.nodeTag] = parent;
7171
}
72+
[self setNeedsUpdate];
7273
}
7374

7475
- (void)onDetachedFromNode:(RCTAnimatedNode *)parent
@@ -79,6 +80,7 @@ - (void)onDetachedFromNode:(RCTAnimatedNode *)parent
7980
if (parent) {
8081
[_parentNodes removeObjectForKey:parent.nodeTag];
8182
}
83+
[self setNeedsUpdate];
8284
}
8385

8486
- (void)detachNode
@@ -93,10 +95,6 @@ - (void)detachNode
9395

9496
- (void)setNeedsUpdate
9597
{
96-
if (_needsUpdate) {
97-
// Has already been marked. Stop branch.
98-
return;
99-
}
10098
_needsUpdate = YES;
10199
for (RCTAnimatedNode *child in _childNodes.allValues) {
102100
[child setNeedsUpdate];
@@ -105,9 +103,7 @@ - (void)setNeedsUpdate
105103

106104
- (void)cleanupAnimationUpdate
107105
{
108-
if (_hasUpdated) {
109-
_needsUpdate = NO;
110-
_hasUpdated = NO;
106+
if (!_needsUpdate) {
111107
for (RCTAnimatedNode *child in _childNodes.allValues) {
112108
[child cleanupAnimationUpdate];
113109
}
@@ -116,7 +112,7 @@ - (void)cleanupAnimationUpdate
116112

117113
- (void)updateNodeIfNecessary
118114
{
119-
if (_needsUpdate && !_hasUpdated) {
115+
if (_needsUpdate) {
120116
for (RCTAnimatedNode *parent in _parentNodes.allValues) {
121117
[parent updateNodeIfNecessary];
122118
}
@@ -126,7 +122,7 @@ - (void)updateNodeIfNecessary
126122

127123
- (void)performUpdate
128124
{
129-
_hasUpdated = YES;
125+
_needsUpdate = NO;
130126
// To be overidden by subclasses
131127
// This method is called on a node only if it has been marked for update
132128
// during the current update loop

Libraries/NativeAnimation/Nodes/RCTStyleAnimatedNode.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ - (void)performUpdate
3838
NSDictionary<NSString *, NSNumber *> *style = self.config[@"style"];
3939
[style enumerateKeysAndObjectsUsingBlock:^(NSString *property, NSNumber *nodeTag, __unused BOOL *stop) {
4040
RCTAnimatedNode *node = self.parentNodes[nodeTag];
41-
if (node && node.hasUpdated) {
41+
if (node) {
4242
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) {
4343
RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node;
4444
[self->_updatedPropsDictionary setObject:@(parentNode.value) forKey:property];

Libraries/NativeAnimation/Nodes/RCTTransformAnimatedNode.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ - (void)performUpdate
4646
NSNumber *nodeTag = transformConfig[@"nodeTag"];
4747

4848
RCTAnimatedNode *node = self.parentNodes[nodeTag];
49-
if (node.hasUpdated && [node isKindOfClass:[RCTValueAnimatedNode class]]) {
49+
if ([node isKindOfClass:[RCTValueAnimatedNode class]]) {
5050
RCTValueAnimatedNode *parentNode = (RCTValueAnimatedNode *)node;
5151

5252
NSString *property = transformConfig[@"property"];

Libraries/NativeAnimation/Nodes/RCTValueAnimatedNode.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ - (instancetype)initWithTag:(NSNumber *)tag
2727
_value = [self.config[@"value"] floatValue];
2828
}
2929
return self;
30+
3031
}
3132

3233
- (void)flattenOffset

Libraries/NativeAnimation/RCTNativeAnimatedModule.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ - (void)setBridge:(RCTBridge *)bridge
5151
_propAnimationNodes = [NSMutableSet new];
5252
}
5353

54-
5554
- (dispatch_queue_t)methodQueue
5655
{
5756
return dispatch_get_main_queue();
@@ -218,6 +217,8 @@ - (dispatch_queue_t)methodQueue
218217
if (viewTag && [node isKindOfClass:[RCTPropsAnimatedNode class]]) {
219218
[(RCTPropsAnimatedNode *)node connectToView:viewTag animatedModule:self];
220219
}
220+
[node setNeedsUpdate];
221+
[node updateNodeIfNecessary];
221222
}
222223

223224
RCT_EXPORT_METHOD(disconnectAnimatedNodeFromView:(nonnull NSNumber *)nodeTag

0 commit comments

Comments
 (0)