Sam Matthews
Sam Matthews

Reputation: 677

Animation - How to turn animated values into real values via interpolate

I'm trying to simultaneously restrict the values of my animation (using clamp as well as interpolate) but also get the values out of the interpolation so I can use them. Specifically because I want to update a piece of state with them and create an observable from that. I just can't figure out how to extract the 'real' value out of the AnimatedValue that is produced by the interpolation (in this case state.panValue). I've tried

this.state.panValue.value 

and

this.state.panValue._value 

and they come back as undefined. If anyone could help me out would be amazing!

EDIT: I'd also be really happy to just have the

this.state.pan.x

variable updated within the limits so I can skip the whole updating the state variable 'panValue' thing. A nice guy on Facebook suggested that I could implement this limit somehow inside the onPanResponderMove by switching the variable to a function or something but I've tried several things and all I get are errors, I guess because I don't really know how to 'safely' amend these animated values.

      onPanResponderMove: Animated.event([
      null,
      { dx: this.state.pan.x },
    ]),

Original Code:

import React, { Component } from 'react';
import {
  View,
  Animated,
  PanResponder,
  Text,
} from 'react-native';
import styles from './styles';

class ClockInSwitch extends Component {
  constructor(props) {
   super(props);
   this.state = {
   pan: new Animated.ValueXY(),
   panValue: 0,
  };
 }

componentWillMount() {
 this._animatedValueX = 0;
 this._animatedValueY = 0;
 this.state.pan.x.addListener((value) => {
   this._animatedValueX = value.value;
    this.setState({
        panValue: this.state.pan.x.interpolate({
          inputRange: [-30, 0, 120,],
          outputRange: [-10, 0, 120,],
          extrapolate: 'clamp',
        }),
    });
});

this._panResponder = PanResponder.create({
  // Ask to be the responder:
  onStartShouldSetPanResponder: (evt, gestureState) => true,
  onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
  onMoveShouldSetPanResponder: (evt, gestureState) => true,
  onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,

  onPanResponderGrant: (evt, gestureState) => {
    this.state.pan.setOffset({
      x: this._animatedValueX,
    });
    this.state.pan.setValue({ x: 0, y: 0 });
  },
  onPanResponderMove: Animated.event([
      null,
      { dx: this.state.pan.x },
    ]),
  onPanResponderTerminationRequest: (evt, gestureState) => true,
  onPanResponderRelease: (evt, gestureState) => {
    this.state.pan.flattenOffset();
    Animated.timing(this.state.pan, {
      toValue: 0,
      duration: 500,
    }).start();
  },
  onPanResponderTerminate: (evt, gestureState) => {
  },
  onShouldBlockNativeResponder: (evt, gestureState) => {
    return true;
  },
 });
}

componentWillUnMount() {
  this.state.pan.x.removeAllListeners();
}

render() {
   const animatedStyle = {
    transform: [{
    translateX: this.state.panValue,
    },
    ],
  };
return (
  <View>
    <Text>{this.state.pan.x._value}</Text>
    <View style={styles.buttonStyle}>
      <Animated.View
        style={[styles.sliderButtonStyle, animatedStyle]}
        {...this._panResponder.panHandlers}
      />
    </View>
  </View>
   );
  }
}

export default ClockInSwitch;

Upvotes: 2

Views: 4690

Answers (1)

Rodzilla
Rodzilla

Reputation: 215

I think this is what you're looking for. I'm using exponent so your declaration for vector icons would probably need to be changed. Cheers!

/**
 * @providesModule ClockInSwitch
 * @flow
 */
import React, {Component} from 'react';
import {View, Animated, StyleSheet, PanResponder, Text} from 'react-native';
import {FontAwesome} from '@exponent/vector-icons';

export class ClockInSwitch extends Component {
    constructor(props) {
        super(props);
        this.state = {
            pan: new Animated.ValueXY(),
            panValue: 0
        };
    }
    componentWillMount() {
        this._panResponder = PanResponder.create({
            onMoveShouldSetResponderCapture: () => true,
            onMoveShouldSetPanResponderCapture: () => true,
            onPanResponderGrant: (e, gestureState) => {
                this
                    .state
                    .pan
                    .setValue({x: 0, y: 0});
            },
            //here's where you can check, constrain and store values
            onPanResponderMove: (evt, gestureState) => {
                // 300 is the width of the red container (will leave it to you to calculate this
                // dynamically) 100 is the width of the button (90) plus the 5px margin on
                // either side of it (10px total)
                var newXVal = (gestureState.dx < 300 - 100)
                    ? gestureState.dx
                    : 300 - 100;
                this
                    .state
                    .pan
                    .x
                    .setValue(newXVal);
                //set this state for display
                this.setState({panValue: newXVal});
            },

            onPanResponderRelease: (e, {vx, vy}) => {
                this
                    .state
                    .pan
                    .flattenOffset();
                Animated
                    .spring(this.state.pan, {
                    toValue: 0,
                    duration: 400,
                    overshootClamping: true
                })
                    .start();
                this.setState({panValue: 0});
            }
        });
    }

    componentWillUnMount() {
        this
            .state
            .pan
            .x
            .removeAllListeners();
    }

    render() {
        //decouple the value from the state object
        let {pan} = this.state;
        let [translateX,
            translateY] = [pan.x, pan.y];
        let translateStyle = {
            transform: [{
                    translateX
                }, {
                    translateY
                }]
        };
        return (
            <View>
                <Text style={styles.leftText}>Power Button Demo</Text>
                <View style={styles.buttonStyle}>
                    <Animated.View
                        style={[styles.sliderButtonStyle, translateStyle]}
                        {...this._panResponder.panHandlers}>
                        <FontAwesome
                            name="power-off"
                            color="#EA2E49"
                            style={{
                            alignSelf: "center",
                            marginHorizontal: 10
                        }}
                            size={36}/>

                    </Animated.View>
                </View>
                <Text style={styles.rightText}>{this.state.panValue}: x value</Text>
            </View>
        );
    }
}

export default ClockInSwitch;
const styles = StyleSheet.create({
    sliderButtonStyle: {
        borderColor: '#FCFFF5',
        borderStyle: 'solid',
        borderWidth: .5,
        backgroundColor: '#FCFFF5',
        borderRadius: 45,
        height: 90,
        width: 90,
        justifyContent: 'center',
        textAlign: 'center',
        marginHorizontal: 5,
        shadowColor: '#333745',
        shadowOffset: {
            width: 2,
            height: 2
        },
        shadowOpacity: .6,
        shadowRadius: 5
    },
    buttonStyle: {
        borderColor: '#FCFFF500',
        backgroundColor: '#DAEDE255',
        borderStyle: 'solid',
        borderWidth: 1,
        height: 100,
        width: 300,
        justifyContent: 'center',
        borderRadius: 50,
        margin: 5,
        flexDirection: 'column'
    },
    rightText: {
        justifyContent: 'center',
        textAlign: 'right',
        fontWeight: '100',
        marginHorizontal:15,
        fontSize: 20,
        color: '#FCFFF5',
        marginVertical:25,
        flexDirection: 'column'
    },
    leftText: {
        justifyContent: 'center',
        textAlign: 'left',
        fontWeight: '100',
        marginHorizontal:15,
        fontSize: 24,
        color: '#FCFFF5',
        marginVertical:25,
        flexDirection: 'column'
    }
});

Upvotes: 2

Related Questions