Add explicit useNativeDriver: false to callsites

Summary:
In order to cleanup the callsites that are not using Animated's native driver, we are going to make useNativeDriver a required option so people have to think about whether they want the native driver or not.

I made this change by changing [Animated.js](https://fburl.com/ritcebri) to have this animation config type:

```
export type AnimationConfig = {
  isInteraction?: boolean,
  useNativeDriver: true,
  onComplete?: ?EndCallback,
  iterations?: number,
};
```

This causes Flow to error anywhere where useNativeDriver isn't set or where it is set to false.

I then used these Flow errors to codemod the callsites.

I got the location of the Flow errors by running:
```
flow status --strip-root --json --message-width=0 | jq '.errors | [.[].extra | .[].message | .[].loc | objects | {source: .source, start: .start, end: .end}]'
```

And then ran this codemod:
```
const json = JSON.parse('JSON RESULT FROM FLOW');

const fileLookup = new Map();

json.forEach(item => {
  if (!fileLookup.has(item.source)) {
    fileLookup.set(item.source, []);
  }

  fileLookup.get(item.source).push(item);
});

export default function transformer(file, api) {
  const j = api.jscodeshift;

  const filePath = file.path;
  if (!fileLookup.has(filePath)) {
    return;
  }

  const locationInfo = fileLookup.get(filePath);

  return j(file.source)
    .find(j.ObjectExpression)
    .forEach(path => {
      if (
        path.node.properties.some(
          property =>
            property != null &&
            property.key != null &&
            property.key.name === 'useNativeDriver',
        )
      ) {
        return;
      }

      const hasErrorOnLine = locationInfo.some(
        singleLocationInfo =>
          singleLocationInfo.start.line === path.node.loc.start.line &&
          Math.abs(
            singleLocationInfo.start.column - path.node.loc.start.column,
          ) <= 2,
      );
      if (!hasErrorOnLine) {
        return;
      }

      path.node.properties.push(
        j.property(
          'init',
          j.identifier('useNativeDriver'),
          j.booleanLiteral(false),
        ),
      );
    })
    .toSource();
}

export const parser = 'flow';
```

```
yarn jscodeshift --parser=flow --transform addUseNativeDriver.js RKJSModules react-native-github
```

Followed up with

```
hg status -n --change . | xargs js1 prettier
```

Reviewed By: mdvacca

Differential Revision: D16611291

fbshipit-source-id: 1157587416ec7603d1a59e1fad6a821f1f57b952
This commit is contained in:
Eli White
2019-08-01 16:46:30 -07:00
committed by Facebook Github Bot
parent 2a8c188701
commit 0a68763743
9 changed files with 151 additions and 35 deletions
@@ -403,7 +403,12 @@ const parallel = function(
const delay = function(time: number): CompositeAnimation {
// Would be nice to make a specialized implementation
return timing(new AnimatedValue(0), {toValue: 0, delay: time, duration: 0});
return timing(new AnimatedValue(0), {
toValue: 0,
delay: time,
duration: 0,
useNativeDriver: false,
});
};
const stagger = function(
@@ -57,8 +57,13 @@ exports.examples = [
// Uses easing functions
this.state.fadeAnim, // The value to drive
{
toValue: 1, // Target
duration: 2000, // Configuration
// Target
toValue: 1,
// Configuration
duration: 2000,
useNativeDriver: false,
},
).start(); // Don't forget start!
}
@@ -121,10 +126,19 @@ exports.examples = [
<RNTesterButton
onPress={() => {
Animated.spring(this.anim, {
toValue: 0, // Returns to the start
velocity: 3, // Velocity makes it move
tension: -10, // Slow
friction: 1, // Oscillate a lot
// Returns to the start
toValue: 0,
// Velocity makes it move
velocity: 3,
// Slow
tension: -10,
// Oscillate a lot
friction: 1,
useNativeDriver: false,
}).start();
}}>
Press to Fling it!
@@ -182,18 +196,35 @@ exports.examples = [
timing(this.anims[0], {
toValue: 200,
easing: Easing.linear,
useNativeDriver: false,
}),
Animated.delay(400), // Use with sequence
timing(this.anims[0], {
toValue: 0,
easing: Easing.elastic(2), // Springy
// Springy
easing: Easing.elastic(2),
useNativeDriver: false,
}),
Animated.delay(400),
Animated.stagger(
200,
this.anims
.map(anim => timing(anim, {toValue: 200}))
.concat(this.anims.map(anim => timing(anim, {toValue: 0}))),
.map(anim =>
timing(anim, {
toValue: 200,
useNativeDriver: false,
}),
)
.concat(
this.anims.map(anim =>
timing(anim, {
toValue: 0,
useNativeDriver: false,
}),
),
),
),
Animated.delay(400),
Animated.parallel(
@@ -206,6 +237,7 @@ exports.examples = [
toValue: 320,
easing,
duration: 3000,
useNativeDriver: false,
}),
),
),
@@ -215,8 +247,12 @@ exports.examples = [
this.anims.map(anim =>
timing(anim, {
toValue: 0,
easing: Easing.bounce, // Like a ball
// Like a ball
easing: Easing.bounce,
duration: 2000,
useNativeDriver: false,
}),
),
),
@@ -250,10 +286,19 @@ exports.examples = [
<RNTesterButton
onPress={() => {
Animated.spring(this.anim, {
toValue: 0, // Returns to the start
velocity: 3, // Velocity makes it move
tension: -10, // Slow
friction: 1, // Oscillate a lot
// Returns to the start
toValue: 0,
// Velocity makes it move
velocity: 3,
// Slow
tension: -10,
// Oscillate a lot
friction: 1,
useNativeDriver: false,
}).start();
}}>
Press to Spin it!
@@ -48,8 +48,13 @@ class Circle extends React.Component<any, any> {
this.props.onMove && this.props.onMove(value);
});
Animated.spring(this.state.pop, {
toValue: 1, // Pop to larger size. (step2b: uncomment)
...config, // Reuse config for convenient consistency (step2b: uncomment)
// Pop to larger size. (step2b: uncomment)
toValue: 1,
// Reuse config for convenient consistency (step2b: uncomment)
...config,
useNativeDriver: false,
}).start();
this.setState(
{
@@ -61,8 +66,11 @@ class Circle extends React.Component<any, any> {
onPanResponderRelease: (e, gestureState) => {
LayoutAnimation.easeInEaseOut(); // @flowfixme animates layout update as one batch (step3: uncomment)
Animated.spring(this.state.pop, {
toValue: 0, // Pop back to 0 (step2c: uncomment)
// Pop back to 0 (step2c: uncomment)
toValue: 0,
...config,
useNativeDriver: false,
}).start();
this.setState({panResponder: undefined});
this.props.onMove &&
@@ -185,7 +193,11 @@ class Circle extends React.Component<any, any> {
_toggleIsActive(velocity) {
const config = {tension: 30, friction: 7};
if (this.state.isActive) {
Animated.spring(this.props.openVal, {toValue: 0, ...config}).start(() => {
Animated.spring(this.props.openVal, {
toValue: 0,
...config,
useNativeDriver: false,
}).start(() => {
// (step4: uncomment)
this.setState({isActive: false}, this.props.onDeactivate);
}); // (step4: uncomment)
@@ -193,7 +205,11 @@ class Circle extends React.Component<any, any> {
this.props.onActivate();
this.setState({isActive: true, panResponder: undefined}, () => {
// this.props.openVal.setValue(1); // (step4: comment)
Animated.spring(this.props.openVal, {toValue: 1, ...config}).start(); // (step4: uncomment)
Animated.spring(this.props.openVal, {
toValue: 1,
...config,
useNativeDriver: false,
}).start(); // (step4: uncomment)
});
}
}
@@ -42,12 +42,18 @@ class AnExBobble extends React.Component<Object, any> {
if (this.state.selectedBobble !== null) {
const restSpot = BOBBLE_SPOTS[this.state.selectedBobble];
Animated.spring(this.state.bobbles[this.state.selectedBobble], {
toValue: restSpot, // return previously selected bobble to rest position
// return previously selected bobble to rest position
toValue: restSpot,
useNativeDriver: false,
}).start();
}
if (newSelected !== null && newSelected !== 0) {
Animated.spring(this.state.bobbles[newSelected], {
toValue: this.state.bobbles[0], // newly selected should track the selector
// newly selected should track the selector
toValue: this.state.bobbles[0],
useNativeDriver: false,
}).start();
}
this.state.selectedBobble = newSelected;
@@ -56,7 +62,10 @@ class AnExBobble extends React.Component<Object, any> {
const releaseBobble = () => {
this.state.bobbles.forEach((bobble, i) => {
Animated.spring(bobble, {
toValue: {x: 0, y: 0}, // all bobbles return to zero
// all bobbles return to zero
toValue: {x: 0, y: 0},
useNativeDriver: false,
}).start();
});
};
@@ -65,8 +74,13 @@ class AnExBobble extends React.Component<Object, any> {
onPanResponderGrant: () => {
BOBBLE_SPOTS.forEach((spot, idx) => {
Animated.spring(this.state.bobbles[idx], {
toValue: spot, // spring each bobble to its spot
friction: 3, // less friction => bouncier
// spring each bobble to its spot
toValue: spot,
// less friction => bouncier
friction: 3,
useNativeDriver: false,
}).start();
});
},
@@ -25,7 +25,11 @@ class AnExChained extends React.Component<Object, any> {
const sticker = new Animated.ValueXY();
Animated.spring(sticker, {
...stickerConfig,
toValue: this.state.stickers[i], // Animated toValue's are tracked
// Animated toValue's are tracked
toValue: this.state.stickers[i],
useNativeDriver: false,
}).start();
this.state.stickers.push(sticker); // push on the followers
}
@@ -36,10 +40,15 @@ class AnExChained extends React.Component<Object, any> {
Animated.decay(this.state.stickers[0], {
// coast to a stop
velocity: {x: gestureState.vx, y: gestureState.vy},
deceleration: 0.997,
useNativeDriver: false,
}),
Animated.spring(this.state.stickers[0], {
toValue: {x: 0, y: 0}, // return to start
// return to start
toValue: {x: 0, y: 0},
useNativeDriver: false,
}),
]).start();
};
@@ -82,6 +82,8 @@ class AnExSet extends React.Component<Object, any> {
inputRange: [0, 300], // and interpolate pixel distance
outputRange: [1, 0], // to a fraction.
}),
useNativeDriver: false,
}).start();
},
onPanResponderMove: Animated.event(
@@ -92,7 +94,10 @@ class AnExSet extends React.Component<Object, any> {
this.props.onDismiss(gestureState.vy); // delegates dismiss action to parent
} else {
Animated.spring(this.props.openVal, {
toValue: 1, // animate back open if released early
// animate back open if released early
toValue: 1,
useNativeDriver: false,
}).start();
}
},
@@ -29,7 +29,11 @@ class AnExTilt extends React.Component<Object, any> {
inputRange: [-300, 0, 300], // pan is in pixels
outputRange: [0, 1, 0], // goes to zero at both edges
}),
duration: 0, // direct tracking
// direct tracking
duration: 0,
useNativeDriver: false,
}).start();
},
onPanResponderMove: Animated.event(
@@ -43,10 +47,15 @@ class AnExTilt extends React.Component<Object, any> {
toValue = -500;
}
Animated.spring(this.state.panX, {
toValue, // animate back to center or off screen
velocity: gestureState.vx, // maintain gesture velocity
// animate back to center or off screen
toValue,
// maintain gesture velocity
velocity: gestureState.vx,
tension: 10,
friction: 3,
useNativeDriver: false,
}).start();
this.state.panX.removeAllListeners();
const id = this.state.panX.addListener(({value}) => {
@@ -54,7 +63,10 @@ class AnExTilt extends React.Component<Object, any> {
if (Math.abs(value) > 400) {
this.state.panX.removeListener(id); // offscreen, so stop listening
Animated.timing(this.state.opacity, {
toValue: 1, // Fade back in. This unlinks it from tracking this.state.panX
// Fade back in. This unlinks it from tracking this.state.panX
toValue: 1,
useNativeDriver: false,
}).start();
this.state.panX.setValue(0); // Note: stops the spring animation
toValue !== 0 && this._startBurnsZoom();
@@ -67,8 +79,13 @@ class AnExTilt extends React.Component<Object, any> {
_startBurnsZoom() {
this.state.burns.setValue(1); // reset to beginning
Animated.decay(this.state.burns, {
velocity: 1, // subtle zoom
deceleration: 0.9999, // slow decay
// subtle zoom
velocity: 1,
// slow decay
deceleration: 0.9999,
useNativeDriver: false,
}).start();
}
@@ -494,7 +494,11 @@ exports.examples = [
platform: 'android',
render: function() {
const mScale = new Animated.Value(1);
Animated.timing(mScale, {toValue: 0.3, duration: 1000}).start();
Animated.timing(mScale, {
toValue: 0.3,
duration: 1000,
useNativeDriver: false,
}).start();
const style = {
backgroundColor: 'rgb(180, 64, 119)',
width: 200,
@@ -27,6 +27,7 @@ class Flip extends React.Component<{}, $FlowFixMeState> {
Animated.timing(this.state.theta, {
toValue: 360,
duration: 5000,
useNativeDriver: false,
}).start(this._animate);
};