React Native series

Welcome to Star our React Native Showcase, which documents various react Native interactive and animation effects.

  • Card dynamic light effect
  • Nine squares draw
  • Custom pull-down refresh animation

This article describes how to use shared UI elements to create an animation similar to the Today page on the Apple App Store. Let’s take a look at the final result:

Step 1: Complete the static UI layout

The UI layout is simple, just a plain ScrollView. Let’s go straight to the code:

<ScrollView style={{ flex: 1.paddingTop: 20, }}>
    {
        Images.map((image, index) = > {
            return (
                <View style={{ width: SCREEN_WIDTH.height: SCREEN_HEIGHT - 150.padding: 15}} >
                    <Image
                        source={image}
                        style={{
                            height: null.width: null.borderRadius: 20.flex: 1.resizeMode: 'cover'}} / >
                </View>); })}</ScrollView>
Copy the code

You can also open the Expo Project directly to see the first step.

Step 2: Finish opening the detail page animation

Before we start coding, let’s take a look at a few features of animation:

  • First of all, we should make it clear that from list “page” to detail “page” is not a real page jump (at least the existing react-navigation or react-native navigation cannot realize the jump animation of reusing page UI elements). So the list “page” and the detail “page” are just views in two different states of the same page

  • With the above conclusion, we analyze the animation effect by ourselves: in fact, the whole open detail animation consists of two parts:

    1. Move the detail "page" picture from the current click position and size to the final top position 2. In this process, the image description text position is moved from outside the screen vision to the final positionCopy the code

Let’s see how the code works:

First complete the details “page” layout:

<ScrollView style={{ flex: 1, paddingTop: 20, }}> ... </ScrollView> <View style={StyleSheet.absoluteFill}> <View style={{ flex: 2 }}> <Image source={ this.state.activeImage ? this.state.activeImage : null } style={{ top: 0, right: 0, height: null, width: null, resizeMode: 'cover' }} /> <TouchableWithoutFeedback> <View style={{ position: 'absolute', right: 20, top: 30 }}> <Text style={{ fontSize: 20, color: '#fff' }}>X</Text> </View> </TouchableWithoutFeedback> </View> <View style={{ flex: 1, backgroundColor: '# FFF ',}}> < font style={{fontSize: 24}}>Copy the code

Let’s first hide the list page. The details page is as shown below:

Next we need to animate: image, text introduction content area, close button, etc.

Picture animation effect analysis: when the user clicks on the list picture, it changes from the position and size of the currently clicked picture to the position and size of the picture in the final detail “page”.

We can get its position and size information by clicking the measure method of the current picture Ref, and the code is as follows:


constructor(props) {
    // Get the position information of the current picture when animating
    this.imageRef = [];
    this.state = {
      activeImage: null,}}function openImage(index) {
    this.imageRef[index].measure((x, y, width, height, pageX, pageY) = > {
        // The coordinates of pageX and pageY in the screen
        // width, height
        / /... With the position and size of the image we can start the animation
    });
}

<TouchableWithoutFeedback onPress={() => { this.openImage(index); }} ><Image
        source={image}
        ref={(image)= > { this.imageRef[index] = image; }}
        style={{ height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', }}
    />
</TouchableWithoutFeedback>

Copy the code

Now let’s complete the animation of the image: position, size, rounded corners, etc. :


constructor(props) {
    this.position = new Animated.ValueXY();
    this.measure = new Animated.ValueXY();
    this.animation = new Animated.Value(0);
}

function openImage(index) {
    // Get the information for clicking the image
    this.imageRef[index].measure((x, y, width, height, pageX, pageY) = > {
        this.position.setValue({ x: pageX, y: pageY });
        this.measure.setValue({ x: width, y: height });
        this.setState(
            (a)= > { return { activeImage: Images[index] } },
            () => {
                // Get target location information
                this.imageContainer.measure((x, y, width, height, pageX, pageY) = > {
                    Animated.parallel([
                        Animated.timing(this.position.x, {
                            toValue: pageX,
                            duration: 350.// Add an elastic effect
                            easing: Easing.back(1),
                        }),
                        Animated.timing(this.position.y, {
                            toValue: pageY,
                            duration: 350.easing: Easing.back(1),
                        }),
                        Animated.timing(this.measure.x, {
                            toValue: width,
                            duration: 350.easing: Easing.back(1),
                        }),
                        Animated.timing(this.measure.y, {
                            toValue: height,
                            duration: 350.easing: Easing.back(1),
                        }),
                        Animated.timing(this.animation, {
                            toValue: 1.duration: 350.easing: Easing.back(1), }), ]).start(); }); }); }); }const imageBorderRadiusAnimation = this.animation.interpolate({
    inputRange: [0.1].outputRange: [20.0]});const imageAnimationStyle = {
    left: this.position.x,
    top: this.position.y,
    width: this.measure.x,
    height: this.measure.y,
    borderRadius: imageBorderRadiusAnimation,
};

<TouchableWithoutFeedback onPress={()= >{ this.openImage(index); }} ><Image
        source={image}
        ref={(image)= > { this.imageRef[index] = image; }}
        style={[
            { height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', },
            imageAnimationStyle
        }
    />
</TouchableWithoutFeedback>
Copy the code

This completes the animation of the image, as well as the animation of the text area and the close button. The full code can be found here.

Step 3: Complete the closing detail page animation

Turning off the detail animation is exactly the reverse of turning on the detail animation:

  1. The top position of the image is restored to the position before clicking, so we need to save the position and size information of the clicked image in the list when opening details
  2. Details text introduction area disappears from bottom layer
  3. The close button gradually becomes transparent and disappears

The code and open details are similar, you can refer directly to the final version

advertising

Welcome to star, our renrendai big front end team blog. All the articles will be updated to zhihu column and Nuggets account simultaneously. We will share several high quality big front end technology articles every week.