Replies: 1 comment
-
Hi @ciza99. import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
import {
Gesture,
GestureDetector,
GestureHandlerRootView,
} from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
runOnJS,
useAnimatedStyle,
} from 'react-native-reanimated';
export default function App() {
const [dragging, setDragging] = useState(false);
const absolute = useSharedValue({ x: 0, y: 0 });
const transform = useSharedValue({ x: 0, y: 0 });
const handleDragEnd = () => {
setDragging(false);
};
const gesture = Gesture.Pan()
.onBegin(() => {
runOnJS(setDragging)(true);
})
.onUpdate((event) => {
console.log(event.translationX, event.translationY);
transform.value = {
x: event.translationX,
y: event.translationY,
};
})
.onEnd((event) => {
absolute.value = {
x: event.absoluteX - 50,
y: event.absoluteY - 50,
};
transform.value = {
x: 0,
y: 0,
};
runOnJS(handleDragEnd)();
});
const animatedStyle = useAnimatedStyle(() => {
return {
backgroundColor: dragging ? '#4C1F50FF' : '#F44336FF',
width: 100,
height: 100,
position: 'absolute',
top: absolute.value.y,
left: absolute.value.x,
transform: [
{ translateX: transform.value.x },
{ translateY: transform.value.y },
],
};
});
return (
<View style={styles.container}>
<GestureHandlerRootView>
<GestureDetector gesture={gesture}>
<Animated.View style={animatedStyle} />
</GestureDetector>
</GestureHandlerRootView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#3FA2FFFF',
},
}); Key part in here is setting both RunOnJS is a function that schedules a function to be executed asynchronously on JS thread. Therefore, it might not be executed during the same frame and you see the flickering. Although your case is more complex than my snippet, I think it's what you need. Always try to set SharedValue's value on UI thread if possible since it's a synchronous operation there. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Context:
Hi, I'm creating drag and drop library for my project and I need to sync animation from shared values with state. What I have is a component called
DndContext
which takes a prop calledonDragEnd
. The proponDragEnd
is a callback function taht is triggered whenever theGesture.Pan
ends. It is provided with information about the dragged node and the node that is hovered over. On this callback function I want to be able to change the state, for example move the dragged node into a container if the dragged node is hovering over it at the end of a pan gesture.However whenever I change the state, I see a flicker where the element returns to the initial position before the
setState
takes affect and the layout is re-rendered. When the element is dragged, I apply correct transform withuseAnimatedStyle
to move the element while dragging. So the real question here is how can I reset the transform exactly at the point in time when the new layout will be rendered, so the dragged element does not flicker.Here is the code to get a better idea of what I'm trying to achieve:
Pan Gesture
Here I'm setting the
transform
withonUpdate
andonEnd
I run a functionhandleDragEnd
on the JS thread.handleDragEnd
This function calls the
onDragEnd
function that sets the new state which changes the layout. It also sets dragging tofalse
. I run this in a batch which I thought would prevent the flicker but it did not.useEffect on dragging
And finally, whenever the dragging state changes and is
false
, I reset the transform. TheuseEffect
should run after the render so I though this would be a good place to reset the transform.Should this not work? Or is the synchronization more complex. Please I would be grateful for any help 🙏.
Beta Was this translation helpful? Give feedback.
All reactions