react native animation , Easing.bounce force bounce only two times - react-native

Is it possible to for Easing.bounce to bounce only 2 times?
And not 3 times;
Animated.timing(
this.y_translate,
{
delay: 0,
toValue: 1,
easing: Easing.bounce,
duration: 1000,
}
).start();
});

Short answer is no.
Long answer is to look at the docs.
You'll see that Easing.bounce is based off of this mathematical definition that has the bounce happening 4 times. You can see exactly how they do this in their example code (note the easeInBounce, easeOutBounce, and easeInOutBounce functions). If you compare this with what is in the React Native source for Easing.bounce, you'll find the same mathematical calculations.
So if you want to have a two bounce effect, you'll need to do the math yourself. For example, you can create your own Easing class with it's own bounce method. Then use it in your animation in place of Easing.bounce.
// You can also extend Easing and just define a new method (or override)
// if you want access to Easing's other methods.
class MyEasing {
/**
* Provides a two bounce effect.
*/
static bounce(t: number): number {
/* Code Goes Here */
return something;
}
}
// ... in your code ...
Animated.timing(
this.y_translate,
{
delay: 0,
toValue: 1,
easing: MyEasing.bounce,
duration: 1000,
}
).start();
});
How you do this calculation though is outside the scope of the question, so I'll leave that for your to figure out.

Related

how can I stop the wheel of fortune to particular point using Animated API in React Native?

I'm developing wheel of fortune where I want the wheel to iterate four times while spinning, and stop to a particularly given value. All I want is to spin the wheel four times and stop to a particular slice. I'm thinking below code can work my idea.
Animated.loop(
Animated.timing(this._angle, {
toValue: 920,
duration: 5000,
}),//.start();
{
iterations: 1
}
).start();
I'm using angle as value. My formula to get the 'toValue' is "targetAngle * 4". This works but I want to decelerate the spinning in its last iterate just like Animated.decay() does. Is there any better idea to resolve this issue?
you can do
const onPress = () => {
Animated.timing(RotateAnimeted, {
toValue: 6,
duration: 1000,
}).start();
};
const Rotate = RotateAnimeted.interpolate({
inputRange: [0, 1, 2, 3, 4, 5, 6],
outputRange: [
"0deg",
"360deg",
"720deg",
"840deg",
"940deg",
"1020deg",
"1080deg",
],
});
You can change the values to get the result you want
I did an example at the expo
Attaches a gif image

Stopping Animated timing in react native

I have created a number ticker like odometer in my react native application. But when I click on the button to start the animation it just goes on. I want to stop the animation after like 5 seconds. Here's the part where I think some modification should be done:
componentDidUpdate(prevProps, prevState) {
if (this.props.value !== prevProps.value) {
Animated.timing(this.animation, {
toValue: getPosition(this.props.value, this.props.height),
duration: 500,
useNativeDriver: true,
}).start(function onComplete() {
Animated.timing(this.animation).stop();
});
}
}
Running this throws an error like Cannot read property 'stopAnimation' of undefined.If I remove the onComplete function it just goes on. So,how can I stop this after 5 seconds?
You are using the animation completion callback. It means you are trying to stop something that already ended.
Try
componentDidUpdate(prevProps, prevState) {
if (this.props.value !== prevProps.value) {
Animated.timing(this.animation, {
toValue: getPosition(this.props.value, this.props.height),
duration: 500,
useNativeDriver: true,
}).start();
Animated.timing(this.animation).stop()
}
}
You will stop the animation right after starting it.
The simplest way to stop any kind of animation by calling setValue() method and assign a value.
In your case:
this.animation.setValue(0);
For more details https://reactnative.dev/docs/animatedvaluexy#setvalue
You can use this.animation.stopAnimation();

react-native: Animate position of Animated.View during onPanMove

I have set up an animation and want to animate a View, so when the user touch moves the view follows on the y axis.
I have tried to set this up with Animated.event but it doesn't work. Can somebody please explain how Animated.event works in a panResponder?
This is what I have:
const onPanMove = (evt: GestureResponderEvent, state: PanResponderGestureState) => {
Animated.event([null, {dy: new Animated.Value(state.dy)}], {useNativeDriver: true});
};
But nothing happens. I also googled a lot, but didn't find any good documentation on this.
When I replace the event with timing it works, but it's super laggy since it always waits for the touchmove to pause.
Animated.timing(
this.state.position,
{
toValue: startY - state.dy,
duration: 0,
//easing: this.props.easing,
useNativeDriver: true
}
).start();

Stop a Animated.loop with native driver - so it triggers the start callback

I am running a loop like this:
const anim = new Animated.Value(0);
const res = Animated.loop(
Animated.timing(anim, {
duration: 2000,
toValue: 1,
easing:Easing.inOut(Easing.linear),
useNativeDriver: true,
isInteraction: false
})
);
res.start(e => console.log('anim stopped, e:'));
setTimeout(()=>res.stop(), 5000); // does not trigger callback
// setTimeout(()=>anim.stopAnimation(), 5000); // does not trigger callback
The animation runs, and loops properly. (animated value goes back to 0 at start of each loop).
And the animation stops, if i do either res.stop() or anim.stopAnimation(). But the start callback never triggers. I'm worried that something is still running and I might have a memory leak. Can you please advise on how to properly stop a native driver loop?
Although I couldn't find it in the documentation, there is an optional onComplete key available on the AnimationConfig type (I found it at the top of Animation.js in the repo). The log will now appear when your animation is stopped by adding the callback inside of your configuration:
const res = Animated.loop(
Animated.timing(anim, {
duration: 2000,
toValue: 1,
easing:Easing.inOut(Easing.linear),
useNativeDriver: true,
isInteraction: false,
onComplete: (e) => console.log('anim stopped, e:') // New config option
})
);
There's no guarantee that this won't be a breaking change in the future, but it seems like a pretty safe key name to stick with, so I don't see them refactoring that anytime soon.
As for your concern about the memory leak, the Animated.loop calls the stop() method of the child animation, so you should have no concerns there.

How to make draggable remember it's location in React Native

I'm learning about the animation and panresponder apis in react native. Following Animated Drag and Drop with React Native and the source
I want to extend the example so that you can drag the circle without having to reset it. Currently the code animates the circle back to the center of the screen and relies on the panresponders dX/dY values.
my best guess is I have to change something in onPanResponderMove
onPanResponderMove: Animated.event([null, {
dx: this.state.pan.x,
dy: this.state.pan.y,
}]),
What do I need to change in the source so if I comment out the onPanResponderRelease logic the circle properly drags around the screen?
getTranslateTransform()?
The logic in onPanResponderRelease is making it so that if the draggable is not in the dropzone when released, it is reset back to 0,0 (these are the lines causing it).
Removing that logic alone isn't all you should do though. You should set the offset and reset the value of this.state.pan in onPanResponderRelease. To do this you need to track the value.
In componentDidMount, add this:
this.currentPanValue = {x: 0, y: 0};
this.panListener = this.state.pan.addListener((value) => this.currentPanValue = value);
Then, in componentWillUnmount:
this.state.pan.removeListener(this.panListener);
Now that you have the current value, you just add this to the PanResponder:
onPanResponderRelease: (e, gestureState) => {
this.state.pan.setOffset({x: this.currentPanValue.x, y: this.currentPanValue.y});
this.state.pan.setValue({x: 0, y: 0});
},
It seems more complicated than it actually is. Basically you are just setting the offset from 0/0, then setting the value to 0/0 so that when you start moving it again after having released it before, it doesn't jump back to 0/0 before jumping back to where your finger is. It also makes sure you have its current position...which you will probably need at some point anyways.
Another way would be to track current offset and keep adding it to the pan. So declare this in the constructor currentPanValue : {x: 0, y: 0},
And edit the onPanResponderRelease to the following :
onPanResponderRelease : (e, gesture) => {
this.state.currentPanValue.x += this.state.pan.x._value;
this.state.currentPanValue.y += this.state.pan.y._value;
this.state.pan.setOffset({x: this.state.currentPanValue.x, y: this.state.currentPanValue.y});
this.state.pan.setValue({x: 0, y: 0});
}
Incase if you want the final coordinates, you should get it with this.state.currentPanValue
Actually the only thing you need to change is the onPanResponderRelease function. What you are currently doing is applying the delta of the gesture movement to the initial position of the component. What you want to do is applying it to the position at the end of the last gesture. So you somehow need to save the offset. AnimatedValueXY.setOffset to the rescue!
onPanResponderRelease : (e, gesture) => {
if(this.isDropZone(gesture)){
this.setState({
showDraggable : false
});
}else{
this.state.pan.setOffset({
x: this.state.pan.x._offset + e.dx,
y: this.state.pan.y._offset + e.dy,
})
this.state.pan.setValue({x: 0, y: 0})
}
I'm using the internal _offset variable here because I could find a way to access it via the API. You can off course also just keep track of the offset manually. I didn't test this code yet, but you get the idea.