Search code examples
javascriptreactjsreact-nativereact-animated

Getting a simple scrolling animation to work in react-native


I'm having a hell of a time trying to get a simple scrolling animation working in react native.

What I'm trying to achieve is that the component scrolls off the right hand edge of the screen after it has been tapped.

Whatever I do with this class, I get the error Attempted to assign to readonly property when I tap on the element, and I can't figure out why.

The only assignment I can see happening in the animateOut function is this.anim.offsetX, but that can't be a readonly property, since I clearly assign to it just fine in the constructor.

Things I've tried, but haven't helped:

  1. Take the animatable value out of the state property, so the animated.timing call isn't trying to directly mutate a state property.

  2. Simplify the animate calls so that there's only one animation ever mutating this value.

  3. Carefully read the example at: the reactjs animation docs and see what I'm doing differently.

  4. look for existing questions on stack overflow. I found this question but the error condition there doesn't seem to apply to my code.

Can someone please show me what I'm doing wrong?

    export default class SquareBlock extends Component {

    constructor(props) {
        super(props);
        this.anim = {};
        this.anim.offsetX = new Animated.Value(0);

    }

    animateOut() {
        console.log("animating out");

        console.log("X offset:",this.anim.offsetX)

        Animated.timing(
           this.anim.offsetX,
           { toValue: 120,
             duration: 500
           }
        ).start();


    }


    render() {

        let content = this.props.content || "Tappable";
        let offsetX;

        offsetX = this.anim.offsetX;

        return (
            <Animated.View >
            <TouchableNativeFeedback onPress={ () => this.animateOut() }>
                <View style={ [styles.outerBox, { "left": offsetX } ]  }  >

                    <Text style={ styles.text }> { content }</Text>

                </View>
            </TouchableNativeFeedback>
            </Animated.View>
        )

    }

}

Solution

  • You have to set the value of X and Y and give it to Animated.View

    Moreover, You have to supply a style to animated (atleast style is the name of the prop which animated use).

    Try this:

    Have a position where you want to start in componentWillMount()

    componentWillMount() {
            //important
            this.position = new Animated.ValueXY({x: 30 , y: 0});
    }
    

    In your Animated.View, you have to supply this.position as style //As said, style is the name of the prop which animated take.

    <Animated.View style={this.position.getLayout()}> //very important
        <TouchableNativeFeedback onPress={ () => this.animateOut() }>
                    <View style={ [styles.outerBox, { "left": offsetX } ]  }  >
    
                        <Text style={ styles.text }> { content }</Text>
    
                    </View>
        </TouchableNativeFeedback>
     </Animated.View>
    

    and

    animateOut() {    
             this.position ,
             Animated.timing(this.position, {
                toValue: { x: this.position + 20 , y: 0},
                duration: 250
             }).start();
    }
    

    This should work properly