I have some state variables I want to animate for user micro-interaction. These variables are not properties of a view or style properties; they are actually properties of an SVG such as Circle radius (using react-native-svg). I have used window.requestAnimationFrame to animate these circles and it works buttery smooth but I would like to get features of React.Animated and Easing and maybe reduce some code if this is possible.
So I am struggling with getting Animated.timing(...).start() to update my state for each frame so the rendering will occur.
In an event handler I have something like:
let radiusValue = new Animated.Value(0);
this.setState({ holding: {
radius: radiusValue,
animator: Animated.timing(
radiusValue,
{
toValue: closest.radius*1.15,
duration: 1,
easing: Easing.bounce(3)
}
).start(() => console.log("done"))
}
So that sets up the animation. Now somewhere in my render() code I have:
<Animated.View>
<Svg><Circle cx={x} cy={y} radius={this.state.radius._value}</Svg>
</Animated.View>
Now because my radius Animated.Value is not part of Animated.View props I guess it doesnt generate tween frames. I do get the "done" message from within the Animated.timing(..).start(callback) but obviously since nothing is directly wired up to modifying my state I don't get calls to render for tweening. So how do I get the Animated.timing() to modify my state?
I've tried converting the Svg to an Animated control but this actually crashes the iOS Simulator to home screen (no red screen exception error).
let AnimatedSvg = Animated.createAnimatedControl(Svg);
...
<AnimatedSvg><Circle cx={x} cy={y} radius={this.state.radius._value}</AnimatedSvg>
Is there a callback from Animated.timing(...) that I could set to then call setState()?
The code above is very pseudo, trying to keep the question light.
Related
I'd like to be able to change the toValue of an animation responding to props update. The official docs for React Native Animated API state that the spring method
animates a value according to an analytical spring model based on damped harmonic oscillation. Tracks velocity state to create fluid motions as the toValue updates, and can be chained together.
However, I haven't found anywhere how we can update toValue. Basically, my component looks like this:
const ProgressBar = ({ loadPercentage }) => {
const loadAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
animation.current = spring(loadAnim, {
toValue: loadPercentage,
}).start();
}, [loadAnim, loadPercentage]);
....
}
This doesn't work for all cases. In particular, if loadPercentage changes too often, the component takes up a huge amount of resources. This kinda makes sense, since I'm creating a new animation for each update. Instead, I'd like to simply modify toValue without starting a new animation or anything like that.
This seems pretty basic, but after 4 hours of trying stuff/googling, I give up. -.-
Just in case, I also tried using react-native-reanimated, but no luck there either.
I’m currently using LayoutAnimation to animate a view when children are added. However, since LayoutAnimation causes everything to be animated, globally, and I can’t easily use built-in Animated library to fit my use-case, I’m wondering if react-native-reanimated is able to help.
Here's a snack of my current solution:
https://snack.expo.io/#insats/height-adapation
This is what the result of that looks like:
Is there a way to achieve the same thing without using LayoutAnimation? I've looked through all exampled in react-native-reanimated, and I've read through the docs but I'm still not sure if this is possible to do or how I should get started. I've thought about using Animated to move the item-wrapper out of the viewable area and "scroll" it upwards (using transform translateY) when items are added, but that would require fixed height, which I don't have.
I have 2 approaches that I can suggest out of my mind:
You can configure your LayoutAnimation only when your desired state changed. If you use hooks it would be too easy:
const [state,setState] = useState([]);
useEffect(()=>{
/*rest code*/
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
},[state])
Or if you use class component you can catch your desired state change in componentDidUpdate:
componentDidUpdate(prevProps,prevState){
if(prevState.items!==state.items){
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
}
}
You can use onLayout function of view:
addItem = () => {
this.setState({
items: [...this.state.items, {title:'An item',isNew:true}]
})
};
renderItems = () => {
return this.state.items.map((item, index) => {
let opacity = new Animated.Value(0);
return (
<Animated.View onLayout={({nativeEvent})=>{
if(this.state.item.isNew){
// here you got the height from nativeEvent.layout.height
// Then you have to store the height animate height and opacity to its precise value
// PS I used opacity:0 to calculate the height
}
}} key={index} style={[styles.item,{opacity}>
<Text>{item.title}</Text>
</View>
)
});
};
When it comes to react-native-reanimated I regard it as more faster version of react-native's Animated library. So either way you will have to calculate the height!
We were asked to implement a screen for our app where there would be some data in the form of a list and sections for each data category. When a Section header is pressed, the section data should be expanded or collapsed.
At first, i tried with Listview, and i was changing the datasource each time a header was pressed, but tbh it did not feel like the correct way of doing it.
Creating a custom view, an animating the view's height works ok, but because of the volume of the data, which is big, the initial rendering is a bit slow e.g. there is a noticeable delay when navigating to the screen.
After upgrading to RN 44.3 i was wondering if i could use Sectionlist in a better way than listview.
Generally what's the best way of approaching a requirement like this?
Thanks!
You can animate the items in flatlist/SectionList. A smaple code will look like below (Animates on removing items). You can use same logic for section list as well.
onRemove = () => {
const { onRemove } = this.props;
if (onRemove) {
Animated.timing(this._animated, {
toValue: 0,
duration: ANIMATION_DURATION,
}).start(() => onRemove());
}
};
Refer this link for more details.
I'm using onLayout to detect screen orientation and it's working fine inside my root view, but when I implemented inside the drawer it didn't work, any reason why this happens ?
code :
import Drawer from 'react-native-drawer'
...
onLayout(e) {
console.log('onLayout');
}
<Drawer onLayout={this.onLayout}
It didn't log any thing when orientation changed!
This is because the Drawer component doesn't take onLayout as a prop. You can see in the source code that the rendered View does use onLayout, but it's not pulling from something like this.props.onLayout.
I'm not exactly sure what you're looking to do, but maybe this issue will help you. As it shows, you can pass a function into openDrawerOffset instead of an integer or a ratio in order to be a little more dynamic with how you set your offset:
openDrawerOffset={(viewport) => {
if (viewport.width < 400) {
return viewport.width * 0.1;
}
return viewport.width - 400;
}}
You might also benefit from the Event handlers that react-native-drawer has to offer.
I'm trying implement a 'drag down to dismiss' gesture in my React Native app using Animated.decay to take the velocity from the onPanResponderRelease handler and feed that into the decay.
The only this is I don't really understand what my velocity and deceleration should be. I'm having trouble understanding the meaning of these values. Any values I put in (whether it's the gesturestate.vy from onPanResponderRelease or static values I enter manually) don't result in a fluid animation and the view just disappears immediately.
I'm playing with values like
Animated.decay(this.props.dismissVal, {
velocity: 0.03,
deceleration: 0.997
});
And dismissVal is used like
top: this.props.dismissVal.interpolate({
inputRange: [0, 1],
outputRange: [0, _containerLayout.height]
})
I've looked a lot over the react-native-animated-tinder example, with no luck. I'm having trouble understanding what the value of velocity (and deceleration) should look like.
Do not forget start decay. Animated.decay({...}).start();
And the second mistake could be trying of change this.props. Everything what you pass to component as a prop, can be change only in parent component. In your child component it is final variable.
//constructor
this.state = { animatedValue: new Animated.Value() }
....
....
Animated.decay(this.state.animatedValue, {...}).start();