This was before I realized onStart() wasn’t being called
Using PanGestureHandler and attempting to "drag" an AnimatedView is not working on the web. There are no obvious bugs, the app builds just fine, no warnings in the console when inspecting.
There is one warning that is leading me to believe this could be the source of the problem. I get a warning in the console saying this:
"transform" style array value is deprecated. Use space-separated string functions, e.g., "scaleX(2) rotateX(15deg)".
I am using an AnimatedView with styling from containerStyle to transform the object and move it around when it is being dragged.
The Root of the Problem
So I’ve been looking into it further, trying to debug it and I’ve come to realize the onStart()
callback is not being called. Since the onStart()
callback isn’t being called the context values never end up getting set and the context object overall remains empty. Leading to my initial problem of not being able to drag the object.
This still does work on iOS though. For some reason, on iOS the onStart()
callback is called. This leads to the context being filled and it working.
This is my code so far, keep in mind this is just a component. In the root, I do have a GestureHandlerRootView component wrapping the whole app.
import { View, Image } from 'react-native';
import Animated, {
useAnimatedStyle,
useSharedValue,
useAnimatedGestureHandler,
withSpring,
} from 'react-native-reanimated';
import { PanGestureHandler, TapGestureHandler } from 'react-native-gesture-handler';
const AnimatedImage = Animated.createAnimatedComponent(Image);
const AnimatedView = Animated.createAnimatedComponent(View);
export default function EmojiSticker ({ imageSize, stickerSource }) {
const scaleImage = useSharedValue(imageSize);
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const onDoubleTap = useAnimatedGestureHandler({
onActive: () => {
if (scaleImage.value !== imageSize * 2) {
scaleImage.value = scaleImage.value * 2;
} else {
scaleImage.value = scaleImage.value / 2;
}
},
});
const onDrag = useAnimatedGestureHandler({
onStart: (event, context) => {
context.translateX = translateX.value;
context.translateY = translateY.value;
},
onActive: (event, context) => {
translateX.value = event.translationX + context.translateX;
translateY.value = event.translationY + context.translateY;
},
});
const imageStyle = useAnimatedStyle(() => {
return {
width: withSpring(scaleImage.value),
height: withSpring(scaleImage.value),
};
});
const containerStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateX: translateX.value,
},
{
translateY: translateY.value,
},
],
};
});
return (
<PanGestureHandler onGestureEvent={onDrag}>
<AnimatedView style={[containerStyle, { top: -350 }]}>
<TapGestureHandler onGestureEvent={onDoubleTap} numberOfTaps={2}>
<AnimatedImage
source={stickerSource}
resizeMode='contain'
style={[imageStyle, { width: imageSize, height: imageSize }]}
/>
</TapGestureHandler>
</AnimatedView>
</PanGestureHandler>
);
}
By the way the double tap gesture works perfectly both on the web and iOS.
I am stumped because the dragging works perfectly in iOS just not on the Web. The transform style depreciation has led me to try and figure out a way to create web-specific styling but I’m having a hard time finding anyone else having this issue. I’m sure there’s a real solution to this that I’m just missing. I’m just really confused since it works perfectly on iOS but not on the Web.
I tried to see if anybody else had any issues relating to this and couldn’t really find anything. I also tried looking up the warning I saw in the console.
"transform" style array value is deprecated. Use space-separated string functions, e.g., "scaleX(2) rotateX(15deg)".
I didn’t find anything relating to this either at least when I searched relating it to React-Native
I’m hoping for a solution that makes it draggable on the web.
2
Answers
I actually solved this by looking through the documentation for
react-native-reanimated
. ObviouslyuseAnimatedGestureHandler
isn't deprecated because it worked withonDoubleTap
, not to mentiononDrag
worked just fine on iOS.But going through the documentation on how to handle pan gestures I found this:
So instead of importing
PanGestureHandler
andTapGestureHandler
from 'react-native-gesture-handler' anduseAnimatedGestureHandler
from 'react-native-reanimated' -- one only has to importGesture
andGestureDetector
from 'react-native-gesture-handler'.Gesture
ends up taking the place ofuseAnimatedGestureHandler
whileGestureDetector
takes the place of components likePanGestureHandler
andTapGestureHandler
.I also ended up having to make my own
contextX
andcontextY
variables usinguseSharedValue()
because as far as I could tell, theonBegin()
andonChange()
callbacks didn't have a settable context.Anyways, here is the fixed code that is now working perfectly on both Web and iOS:
If you are using version 2 of React Native Gesture Handler, the new way to archive the onDrag effect could be achieved with a code like:
More or less, the idea is to animate the translation changing it when you detect the gesture. For it, you surround the element with a gesture detector and pass to it a pan configuration. In the pan configuration you save where the item ended (to accommodate for it the next time it pans) and in
onUpdate
you update it to show where it is currently dragged.