$30 off During Our Annual Pro Sale. View Details »

Declarative future of gestures and animations in React Native

Declarative future of gestures and animations in React Native

Introducing react-native-reanimated.

Animations and touch related interactions are the secret ingredients that can make a good app an awesome app. Apparently, they are also the elements most sensitive to frame drops, which degrades the great experience they ought to provide. We will learn what the challenges are when building performant and responsive UI on mobile, and get to know how declarative APIs let us deal with these problems.

Krzysztof Magiera

May 17, 2018
Tweet

More Decks by Krzysztof Magiera

Other Decks in Technology

Transcript

  1. Declarative future of gestures
    and animations in React Native
    Krzysztof Magiera (@kzzzf)

    View Slide

  2. Animations
    hold

    View Slide

  3. Animations
    skip

    View Slide

  4. Animations
    t
    VSync

    View Slide

  5. Animations
    t
    VSync
    ?

    View Slide

  6. Animations
    UI thread
    JS thread
    requestAnimationFrame clb

    View Slide

  7. Animations
    UI thread
    JS queue
    background thread
    readFromStorage
    HTTPResponse

    View Slide

  8. Animations
    UI thread
    JS queue
    background thread
    readFromStorage
    HTTPResponse

    View Slide

  9. Perf monitor

    View Slide

  10. Animated
    state = {
    fade: new Animated.Value(0)
    };
    1. Create value
    style={{ opacity: this.state.fade }}
    />
    2. Hook it up as a view attribute
    Animated.timing(
    this.state.fade,
    {
    toValue: 1,
    }
    ).start();
    3. Animate value
    4. Or attach it to an event
    5. Combine/process values
    const fade = Animated.multiply(
    this.state.factor,
    this.state.scrollY.interpolate({
    inputRange: [0, 300],
    outputRange: [1, 0]
    })
    );
    onScroll={Animated.event([
    { // event.nativeEvent.contentOffset.y
    nativeEvent: {
    contentOffset: {
    y: this.state.scrollY
    }
    }
    }
    ])}
    >

    View Slide

  11. useNativeDriver
    Animated T-minus 2 years

    View Slide

  12. What about gestures?

    View Slide

  13. JS responder system !


    Show this slide
    onPress={this.checkboxPressed}
    disabled={false}
    style={styles.checkbox}
    >
    checked={this.state.checked}
    />



    Show this slide

    View Slide

  14. JS responder system !


    Show this slide
    onPress={this.checkboxPressed}
    disabled={true}
    style={styles.checkbox}
    >
    checked={this.state.checked}
    />



    Show this slide

    View Slide






  15. JS responder system !
    TOUCH DOWN
    TOUCH MOVE
    TOUCH MOVE
    TOUCH MOVE
    TOUCH UP
    JS Thread
    UI Thread

    View Slide






  16. JS responder system !
    TOUCH DOWN
    TOUCH MOVE
    TOUCH MOVE
    TOUCH MOVE
    TOUCH UP
    JS Thread
    UI Thread



    BUBBLING


    View Slide

  17. onScroll={Animated.event(
    [
    { // nativeEvent.contentOffset.y
    nativeEvent: {
    contentOffset: {
    y: this.state.scrollY
    }
    }
    }
    ],
    { useNativeDriver: true }
    )}
    >
    What about gestures?
    this._pan = PanResponder.create({
    onMoveShouldSetPanResponder: YES,
    onMoveShouldSetPanResponderCapture: YES,
    onPanResponderMove: Animated.event(
    [
    null,
    { dx: this._translateValue }
    ],
    { useNativeDriver: false }
    )
    });
    DIRECT BUBBLING

    View Slide

  18. What about gestures?

    View Slide

  19. react-native-gesture-handler
    1.0.0 is out! "
    react-native-gesture-handler
    https://kmagiera.github.io/react-native-gesture-handler/

    View Slide

  20. react-native-gesture-handler
    Handler Parameters Event attributes
    Pan minDist, minPointers translationXY, absoluteXY
    Pinch scale, focalXY, velocity
    Tap
    numberOfTaps,
    maxDuration
    numberOfPointers
    Rotation angle, anchorXY, velocity

    View Slide

  21. ‣Component for each gesture type (pan, pinch, etc)
    ‣Natively backed “touchables”
    ‣Declarative API for defining cross handler interactions
    ‣Extras: drawer, swipeable row, more to come…
    react-native-gesture-handler

    View Slide

  22. react-native-gesture-handler
    onGestureEvent={Animated.event(
    [{ nativeEvent: { rotation: this.rotationValue } }],
    { useNativeDriver: true }
    )}
    >

    ;
    DIRECT

    View Slide

  23. class Snappable extends Component {
    translateX = new Animated.Value(0);
    render() {
    return (

    styles.box,
    { transform: [{ translateX: this.translateX }] }
    ]} />

    );
    }
    }

    View Slide

  24. class Snappable extends Component {
    translateX = new Animated.Value(0);
    render() {
    return (
    onGestureEvent={Animated.event(
    [{ nativeEvent: { translationX: this.translateX } }],
    { useNativeDriver: true }
    )}>
    styles.box,
    { transform: [{ translateX: this.translateX }] }
    ]} />

    );
    }
    }

    View Slide

  25. class Snappable extends Component {
    translateX = new Animated.Value(0);
    render() {
    return (
    onGestureEvent={Animated.event(
    [{ nativeEvent: { translationX: this.translateX } }],
    { useNativeDriver: true }
    )}
    onHandlerStateChange={event N> {
    if (event.nativeEvent.oldState === State.ACTIVE) {
    // Check if the last state was "ACTIVE"
    Animated.spring(this.translateX, {
    toValue: 0,
    velocity: event.nativeEvent.velocityX,
    useNativeDriver: true,
    }).start();
    }
    }}>
    styles.box,
    { transform: [{ translateX: this.translateX }] }
    ]} />

    );
    }
    }

    View Slide

  26. DECLARATIVE
    ALL THE THINGS!!!

    View Slide

  27. Side effects

    View Slide

  28. IF-ELSE node

    View Slide

  29. ImagePreview component challenge
    ‣ Correctly centred pinch
    ‣ Pan with inertia
    ‣ Pan friction on edges
    ‣ Bouncing from edges
    ‣ Pinch friction
    ‣ Bouncing zoom
    ‣ Pinch&Pan simultaneously

    View Slide

  30. ImagePreview component challenge
    IMPE
    RATIVE FREE
    IMPE
    RATIVE FREE

    View Slide

  31. github.com/kmagiera/react-native-reanimated

    View Slide

  32. { // Animated
    this.transX = cond(
    eq(state, State.ACTIVE),
    [
    stopClock(clock),
    set(transX, add(transX, sub(dragX, prevDragX))),
    set(prevDragX, dragX),
    transX,
    ],
    [
    set(prevDragX, 0),
    set(
    transX,
    cond(defined(transX), runSpring(clock, transX, dragVX), 0)
    ),
    ]
    );
    }

    View Slide

  33. Clocks
    export default function decay(clock, state, config) {
    const lastTime = cond(state.time, state.time, clock);
    const deltaTime = sub(clock, lastTime);
    // v0 = v / 1000
    // v = v0 * powf(deceleration, dt);
    // v = v * 1000;
    // x0 = x;
    // x = x0 + v0 * deceleration * (1 - powf(deceleration, dt)) / (1 - deceleration)
    const kv = pow(config.deceleration, deltaTime);
    const kx = divide(
    multiply(config.deceleration, sub(1, kv)),
    sub(1, config.deceleration)
    );
    const v0 = divide(state.velocity, 1000);
    const v = multiply(v0, kv, 1000);
    const x = add(state.position, multiply(v0, kx));
    return [
    set(state.position, x),
    set(state.velocity, v),
    set(state.time, clock),
    cond(lessThan(abs(v), VELOCITY_EPS), set(state.finished, 1)),
    ];
    }

    View Slide

  34. Lower level abstraction
    export const diffClamp = function(a, minVal, maxVal) {
    const value = new AnimatedValue();
    return set(
    value,
    min(
    max(
    add(cond(defined(value), value, a), diff(a)),
    minVal
    ),
    maxVal
    )
    );
    };

    View Slide

  35. ‣ More generic primitive node types
    ‣ Can be used to implement Animated compatible API w/o writing
    specific native code for things like:
    ‣ Complex nodes such as “diffClamp”
    ‣ Interactions such as animated value tracking or animation
    staggering
    ‣ Conditional evaluation & nodes with side effects
    ‣ Less native code & more cross platform JS code
    ‣ No more “useNativeDriver” – all animations runs on UI thread by
    default
    Reanimated highlights

    View Slide

  36. Thank you!
    Krzysztof Magiera
    [email protected]
    @kzzzf

    View Slide