2

I have a React-Native component which size is determined by its content which has to be loaded from the internet. As loading progresses, its size tends to change a bit and I would like this to look a bit smoother.

What I do now which sort of works, is to animate the scaleY property of the view after I get a new height from onLayout. The problem is that I can only set the initial scale for the animation to start from after onLayout, so for a moment, the view is the new size and then starts to animate from the old size to the new size.

If there was a way to get the new layed out size before it renders that would be awesome but I cannot find a way to do this.

Another option would be to put the view in another view that clips it to its old height until its scale can be adjusted but I have no idea how to do this without affecting the size of the view itself. The moment of clip will probably also look strange in its own way.

Gerharddc
  • 3,921
  • 8
  • 45
  • 83
  • I'm not sure if it will work, but you can try to render this view with `position: absolute` outside the screen, then measure it and get new sizes. Hope this helps – savelichalex Apr 04 '18 at 11:57
  • @savelichalex I have also thought about rendering ghosts but I am not crazy about it for this application because I have some fairly heavy rendering here. – Gerharddc Apr 05 '18 at 14:03

1 Answers1

1

I have recently answered a question similar to this issue:
How do you animate the height in react native when you don't know the size of the content?

but I will post the answer here again.
For your case, try running the animate function after you have loaded your content.


Hi everyone, hope it's not too late...

for anyone who is dealing with React Native animation on view's height.
I know it is very annoying as:

✖️ React Native animation seems not supporting layout styles (e.g. width and height)
✖️ LayoutAnimation looks complicated to investigate
✖️ Wish to use an official way to animate instead of installing a third-party package
✖️ Sometimes content could be large to break your view styles

so here is my solution for you (class component way):

First, set the animated value in state:

state = { height: new Animated.Value(0) };

Next, set your animated view's max height with animation interpolation:

const maxHeight = this.state.height.interpolate({ 
  inputRange: [0, 1], 
  outputRange: [0, 2000]  // <-- any value larger than your content's height
};
return (<Animated.View style={[styles.box, { maxHeight: maxHeight }]} />); 
// any other fixed styles in styles.box

After that, set the animation inside the function you called,
or componentDidMount if you want it to show as soon as it rendered:

// or in any function that users interact
componentDidMount() {
  Animated.timing(this.state.formHeight, {
    toValue: 1,
    duration: 500,           // <-- animation duration
    easing: Easing.linear,   // <-- or any easing function
    useNativeDriver: false   // <-- need to set false to prevent yellow box warning
  }).start();
}

Be aware that don't set useNativeDriver to true as it is not supported on layout styles.


Sample

So below is a sample for you to interact with,
feel free to copy and paste to your React Native project to have a try:

import React, { PureComponent } from 'react';
import { Animated, Button, Easing, View, Text, StyleSheet } from 'react-native';

class AnimateBox extends PureComponent {
  state = { opacity: new Animated.Value(0), height: new Animated.Value(0) };

  showContent = () => {
    const { opacity, height } = this.state;

    Animated.timing(height, {
      toValue: 1,
      duration: 500,
      easing: Easing.linear,
      useNativeDriver: false  // <-- neccessary
    }).start(() => {
      Animated.timing(opacity, {
        toValue: 1,
        duration: 500,
        easing: Easing.linear,
        useNativeDriver: false  // <-- neccessary
      }).start();
    });
  };

  render() {
    const { opacity, height } = this.state;
    const maxHeight = height.interpolate({ 
      inputRange: [0, 1], 
      outputRange: [0, 1000]  // <-- value that larger than your content's height
    });

    return (
      <View style={styles.box}>
        <Animated.View style={{ opacity: opacity, maxHeight: maxHeight }}>
          <Text style={styles.content}>
            Lorem Ipsum is simply a dummy text of the printing and typesetting industry.
            Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
            when an unknown printer took a galley of type and scrambled it to make a type specimen book.
            It has survived not only five centuries, but also the leap into electronic typesetting,
            remaining essentially unchanged. It was popularised in the 1960s with the release of
            Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
          </Text>
        </Animated.View>
        <View style={styles.spacing}>
          <Button title="Show content" onPress={this.showContent} />
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  box: {
    backgroundColor: '#fff',
    marginHorizontal: 15,
    paddingHorizontal: 15
  },
  spacing: {
    paddingVertical: 10
  },
  content: {
    fontSize: 16,
    lineHeight: 30,
    color: '#555'
  }
});


export default AnimateBox;

Happy Coding :)

Wing Choy
  • 900
  • 10
  • 25