Can I use the native animation driver to track a pan gesture? - react-native

The React-Native Animated docs show this example as a way to make an Animated.Value (_panX) track a pan gesture:
onPanResponderMove: Animated.event([
null, // raw event arg ignored
{dx: this._panX}, // gestureState arg
]),
Likewise, the Animated.event docs say I can pass a second config parameter, with:
useNativeDriver: Uses the native driver when true. Default false.
Unfortunately, doing this appears to be broken:
onPanResponderMove: Animated.event(..., { useNativeDriver: true });
In particular, because PanResponder.create has a onResponderMove function that does:
if (config.onPanResponderMove) {
config.onPanResponderMove(e, gestureState);
}
Which causes an error, when it treats config.onPanResponderMove as a function, when it's actually an object...
Am I doing something wrong? Or is useNativeDriver not supported when making Animated.Value track a gesture? Would be nice to have something seamlessly track the mouse like that...

Related

Prevent interactions on OpenLayers map while animation is on action

I'm working on a web application that uses OpenLayers map. On this component, I get the properties from its parent, which are used to draw some icons on specified coordinates on the map, and since this takes a bit of a time, to cover that time, I'm using this animation:
map.getView().animate({ center: transform(latitude, longitude], 'EPSG:4326', 'EPSG:3857'), duration: 1000 })
I'm trying to find a solution to prevent any type of mouse interaction on the map, while this animation is in action (not finished).
To deactivate the interactions call a function like disableInteraction():
const disableInteraction = () => {
map.getInteractions().forEach(i => {
i.setActive(false)
})
}
You can call enableInteractions() when the animation stops (or whenever you need), to reactivate the interactions:
const enableInteractions = () => {
map.getInteractions().forEach(i => {
i.setActive(true)
})
}

How to update toValue for a spring animation in React Native (Animated API)?

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.

React Native state update during animation "resets" the animation

I am facing a problem that I've tried to solve in lots of different ways, but I cannot get it to work. Please see this Expo application, I've created a dumb example that demonstrates my problem: https://snack.expo.io/HJB0sE4jS
To summarize, I want to build an app with a draggable component (The blue dot in the example), but while the user drags the component I also need to update the state of the app (the counter in the example). The problem is that whenever the state updates during dragging, the component resets to it's initial position. I want to allow the user to freely drag the component while state updates happen.
I was able to "solve" the issue by putting the PanResponder in a useRef, so it won't be reinitialized in case of a state update, but as you can see in the example, I want to use the state in the PanResponder. If I put it in a useRef I cannot use the state in the PanResponder because it will contain a stale value (it will always contain the initial value of the counter which is 0).
How do you handle these kind of situations in react native? I guess it is not too uncommon that someone wants to update the state during an animation, although I cannot find any documentation or examples on this.
What am I doing wrong?
Edit: I was investigating further and I can see that the problem is that I'm mapping the (dx,dy) values from the gesture parameter to the position, but the (dx,dy) values are reset to (0,0) when the state changes. I guess (dx,dy) initialized to (0,0) when PanResponder is created. Still don't know what to do to make this work...
A mutable ref that holds the latest counter state value, along with a ref to prevent re-initializing the PanResponder should solve the problem in the example:
const [counter] = useCounter();
// Update the counterValue ref whenever the counter state changes
const counterValue = useRef(counter);
useEffect(() => {
counterValue.current = counter;
}, [counter]);
const position = useRef(new Animated.ValueXY());
const panResponder = useRef(PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event(
[null, { dx: position.current.x, dy: position.current.y }],
{ listener: () => console.log(counterValue.current) } // counterValue being a ref, will not go stale
),
onPanResponderRelease: () => {
Animated.spring(position.current, { toValue: { x: 0, y: 0 } }).start();
}
})
);
You can check the above suggestion here: https://snack.expo.io/rkMLgp4jB
I understand though that this is a rather simplified example, and may not work for your actual use-case. It would help if you could share some more details on the actual usage!

React Native Animated.Value/Animated.timing without Animated.View

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.

How to implement, React Native Drag-n-Drop

I want to implement react-native drag-and-drop, to swap component on the drop. supposed there is five component on a screen once I drag it should highlight dropable element. and once it is dropped the component should be swap.
In order to implement drag an drop you need to use the pan responders. First you need to define where are you going to store the values when the object is moving, you can set a property on the state.
state = {
pan: new Animated.ValueXY(),
};
Then you will have to create the pan responders in componentWillMount for example:
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder : () => true,
onPanResponderMove : Animated.event([null,{ // <--- When moving
dx : this.state.pan.x,
dy : this.state.pan.y
}]),
onPanResponderRelease : (e, gesture) => {} // <--- callback when dropped
});
Finally you need to set the left and top to the animated element in the render method.
<Animated.View
{...this.panResponder.panHandlers}
style={[this.state.pan.getLayout(), styles.circle]}>
<Text style={styles.text}>Drag me!</Text>
</Animated.View>
The this.state.pan.getLayout() returns the top and left, you need this in order to move the element around.
In order to swap the elements you need to implement the onPanResponderRelease.
For more detailed steps you can take a look at this tutorial: https://moduscreate.com/blog/animated_drag_and_drop_with_react_native