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();
Related
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();
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.
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.
It will have a callback function called repeatedly to when I start a download task. But It will be so slow that it can't give feedback when touch a button.
Fileio.downloadFile(downloadData[downloadFileIndex].uri, '1.jpg',this.progressFunc.bind(this)).then((DownloadResult)=> {
if (DownloadResult.statusCode == 200) {
let nextDownloadFileIndex = this.props.downloadFileIndex + 1;
this.props.dispatch(DOWNLOADED_A_FILE({
downloadFileIndex:nextDownloadFileIndex,
progressNum:0
}))
}
}).catch((error)=> {
console.log(error)
})
This is my code and the callback function are as be folllowed
progressFunc(DownloadBeginCallbackResult) {
let progressNum = DownloadBeginCallbackResult.bytesWritten / DownloadBeginCallbackResult.contentLength;
if(progressNum<=0.99){
this.props.dispatch(DOWNLOADING_A_FILE({
progressNum:progressNum,
jobId:this.props.jobId
}));
}else{
this.props.dispatch(DOWNLOADING_A_FILE({
progressNum:0,
jobId:this.props.jobId
}));
}
}
I mean I can't get feedback immediately when I touch button. I think that it is because I have a callback function called repeatedly. so js can't handle so many tasks;
It does sound like JS thread is busy doing the request and not able to communicate back to UI thread. One thing you can try is to wrap you on press handler in an InteractionManager.runAfterInteractions(() => ...)
See https://facebook.github.io/react-native/docs/interactionmanager.html
I tried to make a repeated animation, and at first I use the setInterval() to achieve this , but the result seems not well because there is a small break between animations.
And I tried to use the callback in start() :
_rotate(){
this.state.rotation.setValue(0)
Animated.timing(
this.state.rotation,
{
duration:this.speed,
toValue:1,
}
).start( this._rotate.bind(this) )
}
Expected a continuously animation , but the result is just like the situation when using setInterval().
And, when there is no callback in start() , I can use
this.state.rotation.stopAnimation()
to stop the animation. But with the callback , it does not work.
Maybe I missed something. Anyone helps?
After digging into the source code, it seems that Facebook need to do more on the react-native document.
The callback in start should be like this:
//AnimatedImplementation.js
type EndCallback = (result: EndResult) => void;
So , I changed my code to:
_rotate(result){
this.state.rotation.setValue(0)
Animated.timing(
this.state.rotation,
{
duration:this.speed,
toValue:1,
}
).start( this._rotate.bind(this,result) )
}
Then, the problem is gone....