Why would a react native animation be completed before the duration had expired? - react-native

I have a fade animation in my componentDidUpdate function :
if (prevState.fadeStarting !== this.state.fadeStarting){
const fadestart = new Animated.Value(1);
this.setState(
{fadeStarting: false,animFade:fadestart},
() => {
Animated.timing(
this.state.animFade, {
toValue: 0.0,
duration: 100000000,
}
).start(this.endFade())
}
)
}
I assumed that the endFade method would only be called after the duration expired, but the endFade method is called instantly. Is there any reason that this animation would end early?

Could you mean start(this.endFade) instead?
You mean to pass the callback function, not its result (which is evaluated immediately).

Related

React Native (Animations and setState)

I am facing a particular issue related to react native animations with setState (functional component), I have a countdown made with setInterval and each second I make a setState, whenever I have a setState the animation resets and restarts and I don't know why, I am using also useRef like this
const opacity = new Animated.Value(0) const animatedValueRef = useRef(opacity), the animations is looping like this (each 250millis)
Animated.timing(animatedValueRef.current, {
toValue: 1,
duration: 220,
easing: Easing.ease,
useNativeDriver: true,
}).start((event) => {
if(event.finished) {
opacity.setValue(0);
second();
}
});
}
Thank you!
Edit: this is how I implemented the countdown:
function step1(max, onoff) {
let intervalId;
let varCounter = 1;
setAnimation(true); //animation starts
irrigation(); //animation call
let counter = function () {
if (varCounter < max) {
varCounter= varCounter + 1;
setCounter(varCounter + " " + onoff)
} else {
clearInterval(intervalId);
setCounter(" ");
setAnimation(false);
}
};
intervalId = setInterval(()=>{counter()}, 1000);
}
(The code needs to be refactored)
Basically your component is re- render every time your component’s state changes.
The component gets the updated state and React decides if it should re-render the component.
By default React re-renders everything all the time.
all you have to do is chain your animation. Your animated value will go from 0 to 1, then the second animation will make the value from 1 to 0. You just need to start the animation again after 1 second in your counter function. I suggest making the useNativeDriver: false. No need to setState at all.
Animated.timing(animatedValueRef.current, {
toValue: 1,
duration: 220,
easing: Easing.ease,
useNativeDriver: true,
}).start(() => {
Animated.timing(animatedValueRef.current, {
toValue: 0,
duration: 220,
easing: Easing.ease,
useNativeDriver: true,
}).start(())
});
}

How to get current Animated.value when nativeDriver is true?

It is possible to get the current animated value using ._value while nativeDriver is false.
But is it possible to get current value when nativeDriver is turned to true ?
Do we have any work arounds ?
use addListener like so:
this._animatedValue = new Animated.Value(0);
this._animatedValue.addListener(({value}) => this._value = value);
Animated.timing(this._animatedValue, {
toValue: 100,
duration: 500
}).start();
I figured out a workaround for scenarios like this, where the native driver spoils our offset and gives a jump in our animation towards the end.
Create a pseudo animated value (which will not be used for any animation) and keep updating it alongside your original animated value. Finally when your animation is over (like decay, spring, etc.) set the offsets and current angle with the pseudo value as below:
const angle = useRef(new Animated.Value(0)).current; //original value used in animation; will run on native side
const psuedoAngle = useRef(new Animated.Value(0)).current; //only to track the value on JS side
//using PanResponder
...
onPanResponderMove: (evt, gestureState) => {
angle.setValue(some_value);
psuedoAngle.setValue(same_value);
},
onPanResponderRelease: (evt, {vx, vy}) => {
// calculate your velocity
...
const decay1 = Animated.decay(angle,{
velocity: velocity,
deceleration: 0.997,
useNativeDriver: true
}
);
const decay2 = Animated.decay(psuedoAngle,{
velocity: velocity,
deceleration: 0.997,
useNativeDriver: false //this will keep your pseudo value intact
}
);
//set offset and current angle in callback as below
Animated.parallel([decay1,decay2]).start(() => {
psuedoAngle.flattenOffset();
angle.setOffset(psuedoAngle._value); //correct value passed to original value
psuedoAngle.setOffset(psuedoAngle._value);
angle.setValue(0);
psuedoAngle.setValue(0);
});
}

My State value is not Changing ,when button is pressed

onButtonPress = () => {
min = 1000;
max = 9999;
randomotp = min + (Math.random() * (max - min));
console.log(Math.round(randomotp));
this.setState({
otpfield:'#48A23A'
});
console.log('result',this.state.otpfield);
}
}
I have been executing this function in the Button on click , but the corresponding (i,e Otpfield) value is not changing
React does not update the state immediately, it's an asynchronous operation. Therefore your console.log() call is invoked too fast and the state is not yet changed.
Use the "afterUpdated" callback instead (second parameter of the setState method):
this.setState(
{property: true},
() => console.log(this.state.property)
);
The state will not change. onButtonPress function must finish executing first before changing the state.
use componentDidUpdate lifecycle method to check if the state changed
componentDidUpdate(prevProps, prevState) {
console.log('result',this.state.otpfield);
//new State
console.log('result',prevState.otpfield);
// prev State
}

Looping sequences of animations

i am trying to make a sequences of animations on the screen, in which one second one would be looped infinitely. I have a problem calling them properly. I tried to delete .start() from componentDidmount and perform .start(()=>this.animate2) but it messes up whole animation. I can't figure out how can i do it properly.
Thanks in advance!
To simplify the code it looks like this:
animate1(){Animated.sequence([...])}
animate2(){Animated.sequence([...])}
In the componentDidmount i call both of them:
componentDidMount() {
Animated.sequence([
this.animate1(),
Animated.delay(3000),
this.animate2()
]).start();
You only want animate2 to loop forever? What if you tried separating these animations?
componentDidMount() {
Animated.sequence([
this.animate1(),
Animated.delay(3000),
]).start(() => this.animate2());
}
animate2 = () => {
//not sure what this does but you would put that logic here
//for example
Animated.timing(
this.state.fadeAnim,
{
toValue: 1,
}
).start(() => this.animate2()); // recursively call this.animate2
}

React Native Animated singleValue.stopTracking is not a function

I have the following code to animate in React Native
Animated.timing(
this.state.absoluteChangeX,
{toValue: 0},
).start(function() {
this.lastX = 0;
this.lastY = 0;
});
Pretty simple, but whenever it's triggered, I receive the error:
singleValue.stopTracking is not a function
Here's where the error originates:
/react-native/Libraries/Animates/src/AnimtaedImplementation.js
var timing = function(
value: AnimatedValue | AnimatedValueXY,
config: TimingAnimationConfig,
): CompositeAnimation {
return maybeVectorAnim(value, config, timing) || {
start: function(callback?: ?EndCallback): void {
var singleValue: any = value;
var singleConfig: any = config;
singleValue.stopTracking(); // <--------------- HERE!!!
if (config.toValue instanceof Animated) {
singleValue.track(new AnimatedTracking(
singleValue,
config.toValue,
TimingAnimation,
singleConfig,
callback
));
} else {
singleValue.animate(new TimingAnimation(singleConfig), callback);
}
},
stop: function(): void {
value.stopAnimation();
},
};
};
I'm not extremely versed in typeScript, but var singleValue: any means that "singleValue" could be any type. In my case, it's a number. Since numbers don't have methods, it would make sense that this would error.
Am I doing something wrong?
The value you wish to animate must be an instance of Animated.Value, or one of its subtypes. When you initialize your state, it should look something like this:
getInitialState() {
return { absoluteChangeX: new Animated.Value(0) };
}
The fact that the type declaration in the framework method is any is just a lack of constraint, not an explicit invitation to pass any value into it.
See the Animated docs for more examples.
I run into this issue sometimes (React hooks instead) when I forget to set my variable to the .current of the ref:
function MyComponent() {
const animatedValue = useRef(new Animated.Value(0.0)).current; // Notice the .current
}
This may not necessarily answer the original question, but developers who encounter this error while using React hooks may end up here so maybe it will help someone.
I ran into this issue because I used the animated value (2) instead of the object (1):
const animatedValue = useRef(new Animated.Value(0.0)).current; // (1)
const transform = animatedValue.interpolate({
inputRange: [0.0, 1.0],
outputRange: [0, 100]
}); // (2)
Animated.timing(animatedValue, { // USE animatedValue, NOT transform HERE!
toValue: 1.0,
duration: 3000,
});
Hope this can help anyone that was new to React Native Animation (like me :) )...