Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Animations: Swift vs React Native

Animations: Swift vs React Native

How React Native animations work under the hood without dropping frames and a comparison on how to achieve some non-obvious animations in Swift vs React Native.

https://vimeo.com/283450221

Avatar for Flávio Caetano

Flávio Caetano

July 11, 2018
Tweet

More Decks by Flávio Caetano

Other Decks in Programming

Transcript

  1. 1. Getting annoyed by Xcode since 2011 2. Objective-C is

    bae ❤ 3. [myObject retain]; 4. Swift from day 1 5. React Native is my new love
  2. 1. Under the Hood 2. The Animated API in React

    Native 3. Swift vs React Native Animations
  3. JS Context Native Context Initialization JS queue Main queue Dispatch

    UI update Dispatch UI update Dispatch UI update Compute animation Frame 1 Compute animation Frame 2 Compute animation Frame 3
  4. Compute animation Frame 1 Compute animation Frame 2 Compute animation

    Frame 3 Initialization JS Context Native Context JS queue Main queue
  5. JS Context Native Context JS queue Main queue Declaration Initialization

    Compute animation Frame 1 Compute animation Frame 2 Compute animation Frame 3
  6. 1. API is declarative 2. No need to keep bridging

    often 3. Described upon initialization (bridged once) 4. Custom physics driver THE BENEFITS OF WORKING WITH REACT NATIVE
  7. this.state = { animatedOpacity: new Animated.Value(0), } ... <MyComponent style={[

    styles.container, { opacity: this.state.animatedOpacity }, ]}>
  8. <Animated.View style={[ styles.container, { transform: { translateY: this.state.positionYValue.interpolate({ inputRange: [0,

    1], outputRange: [0, 100], }), }, }, ]}> <Text>Foo</Text> <Text>Bar</Text> </Animated.View>
  9. 1. Any number of values you want 2. May have

    easing 3. inputRange.length === outputRange.length 4. inputRange must be sorted INTERPOLATED RULES
  10. UIView.animate( duration: 0.3, animations: { [unowned self] in self.bottomConstraint.constant =

    123 self.view.layoutIfNeeded() }, completed: { [unowned self] _ in UIView.animate( duration: 0.3, animations: { [unowned self] in self.trailingConstraint.constant = 321 self.view.layoutIfNeeded() }, completed: { [unowned self] _ in UIView.animate( duration: 0.3, animations: { [unowned self] in self.button.alpha = 0 } ) } ) } )
  11. UIView.animateKeyframes( withDuration: 0.9, delay: 0, animations: { [unowned self] in

    UIView.addKeyframe( withRelativeStartTime: 2/3, relativeDuration: 1/3, animations: { [unowned self] in self.button.alpha = 0 } ) UIView.addKeyframe( withRelativeStartTime: 0/3, relativeDuration: 1/3, animations: { [unowned self] in self.bottomConstraint.constant = 123 self.view.layoutIfNeeded() } ) UIView.addKeyframe( withRelativeStartTime: 1/3, relativeDuration: 1/3, animations: { [unowned self] in self.trailingConstraint.constant = 321 self.view.layoutIfNeeded() } ) } )
  12. Animated.sequence([ Animated.timing( this.state.positionYValue, { toValue: 1, duration: 300, easing: Easing.bounce,

    } ), Animated.delay(3000), Animated.timing( this.state.positionYValue, { toValue: 0, duration: 300, easing: Easing.in, } ), ]).start()
  13. <CircleView style={{ transform: [{ scale: this.state.scaleValue.interpolate({ inputRange: [0, 1, 2],

    outputRange: [0.3, 0.5, 1], }), }], }}> <Animated.Image style={{ transform: [{ scale: this.state.scaleValue.interpolate({ inputRange: [0, 1, 2], outputRange: [0.65, 0.7, 1], }), }], }} source={require('logo')} /> </CircleView>
  14. <View style={style.menu}> {this.props.menuItems.map((item, idx) => ( <MenuItem item={item} style={{ transform:

    [{ translateY: this.state.positionValue.interpolate({ inputRange: [idx * 0.2, 1 + idx * 0.2], outputRange: [MenuItem.height, 0], extrapolate: 'clamp', easing: Easing.out, } ), }], }} /> ))} </View>
  15. Animated.timing(this.state.positionValue, { duration: 240 + ( 60 * this.props.menuItems.length ),

    toValue: 0.8 + ( 0.2 * this.props.menuItems.length ), }).start()
  16. const StoriesNavigator = createStackNavigator( { Story: { screen: Story }

    }, { transitionConfig: () => ({ transitionSpec: { duration: 500, easing: Easing.out, timing: Animated.timing, }, screenInterpolator: sceneProps => { const { position, scene } = sceneProps const { index } = scene const rotateY = position.interpolate({ inputRange: [index - 0.5, index, index + 0.5], outputRange: ['90deg', '0deg', ‘-90deg'], extrapolate: 'clamp', }) return { transform: [{ rotateY }] } }, }), } )
  17. class CustomTransition extends Component { render() { return ( <Transitioner

    configureTransition={this.configureTransition} navigation={this.props.navigation} render={this.renderTransition} onTransitionStart={() => this.setState({ showButton: false }) } onTransitionEnd={() => this.setState({ showButton: true } } /> ) } configureTransition = () => ({ duration: 500, easing: Easing.inOut, timing: Animated.timing, })
  18. renderTransition = (transitionProps) => { const translateY = transitionProps.position.interpolate({ inputRange:

    [indexOfScreenWithButton, indexOfScreenWithButton+1], outputRange: [100, 300], extrapolate: 'clamp', }) return ( <View style={styles.container}> { transitionProps.scenes.map( this.renderScene(transitionProps) ) } <ButtonSnapshot style={[styles.button, { transform: [{ translateY }] }]} /> </View> ) }
  19. renderScene = ({ position }) => ({ index, route })

    => { const { navigation } = this.props const opacity = position.interpolate({ inputRange: [index-1, index, index+1], outputRange: [0, 1, 0], extrapolate: 'clamp', }) const Scene = navigation.router.getComponentForRouteName(route.routeName); return ( <Animated.View style={[styles.sceneContainer, { opacity }]} key={route.key} > <Scene navigation={navigation} showButton={this.state.showButton} /> </Animated.View> ) }
  20. 1. It just works ™ 2. The API is simpler

    3. Infinite customizations 4. Screen transitions 5. Animations are First-Class citizen TAKEAWAYS
  21. Sources React Native Docs https://facebook.github.io/react-native/docs/animations React Navigation Docs https://reactnavigation.org/docs/en/transitioner.html#docsNav Custom

    Transitions By Daniel Merril https://medium.com/async-la/custom-transitions-in-react-navigation-2f759408a053 Shared Element Transition By Linton YE http://www.reactnativediary.com/2017/01/23/react-navigation-shared-element-transition-1.html Source Code https://github.com/fjcaetano/react-native-animations Slides https://speakerdeck.com/fjcaetano