Replies: 26 comments 9 replies
-
Hi @krszwsk have you found a solution on this? I have the same problem #445 |
Beta Was this translation helpful? Give feedback.
-
Hello @krszwsk @neiker <TapGestureHandler
ref={this.tapRef}
maxDurationMs={1000}
>
<Animated.View>
<PanGestureHandler
waitFor={this.tapRef}
onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}>
<Animated.View
style={[
styles.box,
{
transform: [
{ translateX: this._translateX },
{ translateY: this._translateY },
],
},
this.props.boxStyle,
]}
/>
</PanGestureHandler>
</Animated.View>
</TapGestureHandler> |
Beta Was this translation helpful? Give feedback.
-
@osdnk thank you for your response, but in my case seems don't work because my press and hold elements are inside a This is my code: render() {
const { widget } = this.props;
const { dragging } = this.state;
return (
<TapGestureHandler
onHandlerStateChange={this._onTapHandlerStateChange} // This will made the widget `selected`
ref={this.tapRef}
maxDurationMs={500}
numberOfTaps={1}
maxDist={5} // User move they finger so ScrollView must receive the event
>
<Animated.View>
<PanGestureHandler
onGestureEvent={this._onPanGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}
minPointers={1}
maxPointers={1}
waitFor={widget.selected ? undefined : this.tapRef} // Wait only if the widget is not selected
>
<Animated.View
style={[
styles.item,
{
transform: [
{ translateX: this._translateX },
{ translateY: this._translateY },
],
},
]}
>
<Widget widget={widget} dragging={dragging} />
</Animated.View>
</PanGestureHandler>
</Animated.View>
</TapGestureHandler>
);
} Here is where I'm testing this: |
Beta Was this translation helpful? Give feedback.
-
So maybe try to set |
Beta Was this translation helpful? Give feedback.
-
@osdnk do you mean to set render() {
const { widget } = this.props;
const { dragging } = this.state;
return (
<TapGestureHandler
onHandlerStateChange={this._onTapHandlerStateChange} // This will made the widget `selected`
ref={this.tapRef}
maxDurationMs={500}
numberOfTaps={1}
maxDist={10}
>
<Animated.View>
<PanGestureHandler
onGestureEvent={this._onPanGestureEvent}
onHandlerStateChange={this._onHandlerStateChange}
minPointers={1}
maxPointers={1}
waitFor={widget.selected ? undefined : this.tapRef} // Wait only if the widget is not selected
minDist={9}
>
<Animated.View
style={[
styles.item,
{
transform: [
{ translateX: this._translateX },
{ translateY: this._translateY },
],
},
]}
>
<Widget widget={widget} dragging={dragging} />
</Animated.View>
</PanGestureHandler>
</Animated.View>
</TapGestureHandler>
);
} |
Beta Was this translation helpful? Give feedback.
-
@osdnk can you reopen the issue so it's more visible? |
Beta Was this translation helpful? Give feedback.
-
I have the same issue too. The Alternatively, is there a method to make |
Beta Was this translation helpful? Give feedback.
-
I'm trying do some drag and drop within a scroll view initiated by long press too. |
Beta Was this translation helpful? Give feedback.
-
My code: return (
<View style={styles.body}>
<PanGestureHandler
waitFor={this.tapRef}
onGestureEvent={this._onGestureEvent}
onHandlerStateChange={this._onPanHandlerStateChange}>
<Animated.View>
<TapGestureHandler
ref={this.tapRef}
onHandlerStateChange={this._onTapHandlerStateChange}
key={item.key}
numberOfTaps={1}
maxDurationMs={2000}
>
<Animated.View
style={{
width: Dimensions.get('window').width/3,
height: Dimensions.get('window').width/3,
transform: [
{ translateX: this._translateX },
{ translateY: this._translateY },
]
}}
>
<Craving info={item} showName={this.props.showNames}/>
</Animated.View>
</TapGestureHandler>
</Animated.View>
</PanGestureHandler>
</View>
); I attempted to use this without a NOTE: Switched the places of Tap and Pan gesture handler this cause Tap to change state to BEGAN before the pan, but this did not solve the issue where pan does not wait for tap even though tap is in BEGAN state. Any help would be greatly appreciate. |
Beta Was this translation helpful? Give feedback.
-
I also tried playing around with Decided to swap the long press for a double tap to initiate the drag mode on elements. |
Beta Was this translation helpful? Give feedback.
-
I am also looking to activate my PanHandler after a LongPress in a scroll view. |
Beta Was this translation helpful? Give feedback.
-
I'm trying to achieve a similar effect: long-press items inside <LongPressGestureHandler
simultaneousHandlers={this.props.panRef}
onHandlerStateChange={this.handleLongPressStateChange}
>
<Animated.View>
<PanGestureHandler
ref={this.panRef}
maxPointers={1}
onGestureEvent={this.handleGestureEvent}
onHandlerStateChange={this.handleGestureEvent}
>
<Animated.View
style={{ transform: [{ translateX: this.translateX }, { translateY: this.translateY }] }}
>
<Text style={styles.entryText}>
Hello World
</Text>
</Animated.View>
</PanGestureHandler>
</Animated.View>
</LongPressGestureHandler> Then, the condition for calculating Y-position would be: translateY = cond(
and(eq(this.panState, State.ACTIVE), eq(this.longPressState, State.ACTIVE)),
[/* ... */],
[/* ... */],
) Now, I've been stuck at the What I'm trying to achieve is something like reordering feature in Any.do or Todoist. |
Beta Was this translation helpful? Give feedback.
-
@kimdhoe have you found a workaround by now by any chance? |
Beta Was this translation helpful? Give feedback.
-
Hey everyone, I've finally found a way to implement drag-and-drop on long-press inside the Anyway, I hope my code snipped would help you to deal with your particular scenario. I found out it's, most likely, impossible to use both
import React from 'react'
import { Animated, FlatList, Platform, View } from 'react-native'
import { LongPressGestureHandler, State } from 'react-native-gesture-handler'
import Modal from 'react-native-modal'
const MEASURE_TIMEOUT = Platform.select({
android: 300,
ios: 100,
})
class DraggableList extends React.Component {
// translateX and translateY are not calculated for LongPressGestureHandler
// This variables are used to get them
x0 = null
y0 = null
listItemRefs = {}
listItemMeasurements = {}
measureTimeouts = {}
// stores the position of our dragged element
translate = new Animated.ValueXY({ x: 0, y: 0 })
state = {
dragging: null,
}
onGestureEvent = (event) => {
const { x, y } = event.nativeEvent
// calculate and set translationXY variables while dragging
const translateX = x - this.x0
const translateY = y - this.y0
this.translate.setValue({ x: translateX, y: translateY })
}
onHandlerStateChange = (event) => {
const { state } = event.nativeEvent
if (state === State.ACTIVE) {
this.onDragStart(event)
}
if (state === State.END || state === State.CANCELLED) {
this.onDragEnd(event)
}
}
onDragStart = (event) => {
const { dragging } = this.state
const { x, y } = event.nativeEvent
// Finds the dragged list item
// and renders it's copy over the FlatList
const itemKey = this.findTouchedItemKey(x, y)
if (itemKey && dragging !== itemKey) {
this.x0 = x
this.y0 = y
this.setState({ dragging: itemKey })
}
}
onDragEnd = (event) => {
// Handle a momement when user drop the item somewhere
// Clear initial touch gesture coordinates
this.x0 = null
this.y0 = null
}
removeHoverComponent = () => {
this.setState({ dragging: null })
}
findTouchedItemKey = (touchX, touchY) => {
const itemKeys = Object.keys(this.listItemRefs)
const touchedItemKey = itemKeys.find((itemKey) => {
const shape = this.listItemMeasurements[itemKey]
if (!shape) {
return false
}
return checkPointBoxIntersection(touchX, touchY, shape)
})
return touchedItemKey
}
measureAll = () => {
// Coordinates are recalculated once their position
// on the viewport changes. Otherwise, it's impossible
// to find and highlight the touched item
const itemKeys = Object.keys(this.listItemRefs)
itemKeys.forEach(this.measureListItem)
}
measureListItem = (key) => {
// setTimeout is required, otherwise all measurements will be 0
if (this.measureTimeouts[key]) {
clearTimeout(this.measureTimeouts[key])
}
this.measureTimeouts[key] = setTimeout(
this._measureListItem(key),
MEASURE_TIMEOUT
)
}
_measureListItem = (key) => () => {
const { dragging } = this.state
const element = this.listItemRefs[key]
if (!element || dragging) {
return
}
element.measureInWindow(this._onItemMeasured(key))
}
_onItemMeasured = (key) => (x, y, width, height) => {
this.listItemMeasurements[key] = { x, y, width, height }
}
setListItemRef = (key) => (ref) => {
this.listItemRefs[key] = ref
// Each item is measured, so it's possible
// to calculate which list item is actually dragged
// In my case I have a multicolumn FlatList, so
// I use x, y, width and height
this.measureListItem(key)
}
renderItem = ({ item, index }) => {
const { draggedItemStyle, renderItem, keyExtractor } = this.props
const { dragging } = this.state
const itemKey = keyExtractor(item)
const active = itemKey === dragging
const ListItem = renderItem({ item, index, active })
const wrapperStyle = []
if (active) {
// style your dragged item
wrapperStyle.push(draggedItemStyle)
}
// `collapsable` prop is a must for Android
// without it the measureInWindow won't work
// as the View can be removed from the UI tree
// as an optimization
return (
<View
collapsable={false}
style={wrapperStyle}
ref={this.setListItemRef(itemKey)}
>
{ListItem}
</View>
)
}
renderHoverDraggingElement = () => {
const { data, renderItem } = this.props
const { dragging } = this.state
// That's a hack for my particular case
const index = data.findIndex((i) => i.id === dragging)
const item = data[index]
const ListItem = renderItem({ item, index, active: true })
const { x, y } = this.listItemMeasurements[dragging]
const style = {
position: 'absolute',
left: x,
top: y,
transform: [...this.translate.getTranslateTransform()],
}
return <Animated.View style={style}>{ListItem}</Animated.View>
}
render() {
const { dragging } = this.state
// For Android maxDist is set to Number.MAX_SAFE_INTEGER
// with default value, the drag gesture will be canceled
// https://kmagiera.github.io/react-native-gesture-handler/docs/handler-longpress.html#maxdist
return (
<>
<LongPressGestureHandler
maxDist={Number.MAX_SAFE_INTEGER}
onGestureEvent={this.onGestureEvent}
onHandlerStateChange={this.onHandlerStateChange}
>
<FlatList
{...this.props}
onMomentumScrollEnd={this.measureAll}
renderItem={this.renderItem}
/>
</LongPressGestureHandler>
<Modal
isVisible={Boolean(dragging)}
animationIn="fadeIn"
animationOut="fadeOut"
hasBackdrop={false}
style={styles.modal}
>
{dragging ? this.renderHoverDraggingElement() : <View />}
</Modal>
</>
)
}
} If you need to rearrange items order, take a look at the next repositories: react-native-sortable-grid, react-native-draggable-flatlist. I wouldn't use them as they both look abandoned, but maybe they would help you to come out with a solution. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the approach suggestion @kulyk ! For those using the LongPressGestureHandler on individual views, I found that for Android I also needed to add |
Beta Was this translation helpful? Give feedback.
-
While I'm still stuck at the long press problematic, I might have a solution for you @kimdhoe . For enabling scrolling while dragging, I check the scroll positions distance to the edges of the screen and then use the scrollTo method of the scrollview: cond(and(
eq(state, State.ACTIVE), greaterOrEq(absoluteY, windowHeight - 50)),
call(
[currentScrollPosition],
([value]) => store.scrollView.getNode().scrollTo({ x: 0, y: value + 10, animated: true })
)) I use an Animated.Value to track the currentScrollPosition. I also add the scrollPosition to the items while scrolling so it remains on the edge of the screen visually, it works really smoothly Edit: I missunderstood, you also only want to activate the pan handler after a long press and have the scrollview consume touches otherwise. |
Beta Was this translation helpful? Give feedback.
-
Did anyone find a working solution to this? |
Beta Was this translation helpful? Give feedback.
-
Hi all! I know this is a pretty old issue, but since RN Reanimated 2 is coming out I thought I'd test it with this "drag-and-drop on long press". The intention was to emulate the "up next" queue like that in the iOS music app. I tried building it as a higher-order component here. Below is a screencast of it in action. It allows reordering elements in the list, adding/removing elements, and also scrolling in the parent ScrollView (since scrolling causes the long press to be rejected, the scroll view inherits the responder). Disclaimer: it's not tested on Android at all. My implementation uses a ScrollView rather than a Flatlist (I'm sure it could be refactored), but it's the same general idea as @kulyk's solution: It uses the LongPressGestureHandler's onActive trigger as a substitute for the PanGestureHandler. It also stores all of the children offsets in a class external to the React state, and completely decouples the ordering of the rendered children from their positions. Because of this, in order to get the real ordering of the child elements, you need to use a method exposed by the component. This is triggered by the "Print IDs" button in my example. Screen.Recording.2021-01-15.at.11.02.45.PM.mov |
Beta Was this translation helpful? Give feedback.
-
Seems to work fine for me. Hopefully this helps someone. |
Beta Was this translation helpful? Give feedback.
-
Hello everyone, I too am facing a similar issue. Got a Seems like a pretty straightforward and common use case. Has anyone been able to pull it off without "hacks" or "workarounds"? The docs seem to suggest that the following code should work:
But it doesn't. The |
Beta Was this translation helpful? Give feedback.
-
There's a video on youtube that shows how to implement this with PanGestureHandler: |
Beta Was this translation helpful? Give feedback.
-
I couldn't find a way without having an external const longPressEventHandler = useAnimatedGestureHandler<LongPressGestureHandlerGestureEvent>(
{
onActive: (_) => {
runOnJS(setIsTouched)(true);
},
onEnd: (_) => {
runOnJS(setIsTouched)(false);
},
},
[isTouched]
); Then finally within my onActive: (event, ctx) => {
if (isTouched) {
...code here
}
}, Hope this helps someone even though it's not the cleanest. |
Beta Was this translation helpful? Give feedback.
-
Did you finally achieve it? If so can you please post your answer, I'm still struggling with this!! |
Beta Was this translation helpful? Give feedback.
-
Here is a working version. The code is pretty dirty. <ScrollView style={[{ width: '100%', flex: 1, paddingHorizontal: 8 }]}>
{list.map(item => (
<Item key={item.key} {...{ item }} />
))}
</ScrollView> const Item: React.FC<{ item: IListIitem }> = ({ item }) => {
const isLongPressed = useSharedValue(false);
const isManualPanGestureActive = useSharedValue(false);
const startPointX = useSharedValue(0);
const startPointY = useSharedValue(0);
const prevTranslationX = useSharedValue(0);
const prevTranslationY = useSharedValue(0);
const currentTranslationX = useSharedValue(0);
const currentTranslationY = useSharedValue(0);
const animatedStyles = useSharedValue<AnimatedStyleProp<ViewStyle | ImageStyle | TextStyle>>({
zIndex: 0,
});
const onLongPressStart = useCallback(
(event: GestureStateChangeEvent<LongPressGestureHandlerEventPayload>) => {
console.log('longPress start');
isLongPressed.value = true;
},
[isLongPressed],
);
const longPressGesture = Gesture.LongPress().minDuration(100).onStart(onLongPressStart);
const onManualGestureTouchesDown = useCallback(
(event: GestureTouchEvent, stateManager: GestureStateManagerType) => {
startPointX.value = event.changedTouches[0].absoluteX;
startPointY.value = event.changedTouches[0].absoluteY;
currentTranslationX.value = prevTranslationX.value;
currentTranslationY.value = prevTranslationY.value;
animatedStyles.value = {
transform: [{ translateX: currentTranslationX.value }, { translateY: currentTranslationY.value }],
// zIndex: 1,
};
},
[currentTranslationX, currentTranslationY, startPointX, startPointY, prevTranslationX, prevTranslationY, animatedStyles],
);
const onManualGestureTouchesUp = useCallback(
(event: GestureTouchEvent, stateManager: GestureStateManagerType) => {
isManualPanGestureActive.value = false;
isLongPressed.value = false;
prevTranslationX.value = currentTranslationX.value;
prevTranslationY.value = currentTranslationY.value;
animatedStyles.value = {
transform: [{ translateX: currentTranslationX.value }, { translateY: currentTranslationY.value }],
zIndex: 0,
};
},
[
isManualPanGestureActive,
isLongPressed,
prevTranslationX,
currentTranslationX,
prevTranslationY,
currentTranslationY,
animatedStyles,
],
);
const onManualGestureTouchesMove = useCallback(
(event: GestureTouchEvent, stateManager: GestureStateManagerType) => {
if (isLongPressed.value) {
isManualPanGestureActive.value = true;
}
if (isManualPanGestureActive.value) {
currentTranslationX.value = event.changedTouches[0].absoluteX - startPointX.value + prevTranslationX.value;
currentTranslationY.value = event.changedTouches[0].absoluteY - startPointY.value + prevTranslationY.value;
animatedStyles.value = {
transform: [{ translateX: currentTranslationX.value }, { translateY: currentTranslationY.value }],
zIndex: 1,
};
}
},
[
currentTranslationX,
currentTranslationY,
isLongPressed,
isManualPanGestureActive,
startPointX,
startPointY,
prevTranslationX,
prevTranslationY,
animatedStyles,
],
);
const manualGesture = Gesture.Manual()
.onTouchesMove(onManualGestureTouchesMove)
.onTouchesUp(onManualGestureTouchesUp)
.onTouchesDown(onManualGestureTouchesDown);
const composed = Gesture.Simultaneous(longPressGesture, manualGesture);
const uah = useAnimatedStyle(() => animatedStyles.value);
return (
<GestureDetector gesture={composed}>
<Animated.View style={[uah]} layout={Layout.duration(1000)}>
<Box backgroundColor={item.color} m={8} p={8}>
<Text>{item.name}</Text>
</Box>
</Animated.View>
</GestureDetector>
);
}; |
Beta Was this translation helpful? Give feedback.
-
Guys, My code is not completed/organized yet, I am pasting my code so far, it will get you started. import React, {useRef, useEffect, useState, useCallback} from 'react';
import {
ScrollView,
Text,
StyleSheet,
View,
ImageBackground,
useWindowDimensions,
} from 'react-native';
import {
GestureHandlerRootView,
PanGestureHandler,
PanGestureHandlerGestureEvent,
TapGestureHandler,
LongPressGestureHandler,
State,
} from 'react-native-gesture-handler';
import Animated, {
useAnimatedGestureHandler,
useAnimatedReaction,
useAnimatedStyle,
useAnimatedRef,
useSharedValue,
useAnimatedScrollHandler,
withSpring,
runOnJS,
scrollTo,
cancelAnimation,
} from 'react-native-reanimated';
import {login, google, facebook} from 'src/assets';
import {InputBox, KeyboardHandledView} from 'src/components';
import useStyle from 'src/hooks/useStyle';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Icon1 from 'react-native-vector-icons/Ionicons';
import style from './style';
const listToObject = list => {
const values = Object.values(list);
const object = {};
for (let i = 0; i < values.length; i++) {
object[values[i].id] = i;
}
return object;
};
const DAFT_PUNK = 'Daft Punk';
const ALBUM_COVERS = {
DISCOVERY:
'https://upload.wikimedia.org/wikipedia/en/a/ae/Daft_Punk_-_Discovery.jpg',
HUMAN_AFTER_ALL:
'https://upload.wikimedia.org/wikipedia/en/0/0d/Humanafterall.jpg',
HOMEWORK:
'https://upload.wikimedia.org/wikipedia/en/9/9c/Daftpunk-homework.jpg',
RANDOM_ACCESS_MEMORIES:
'https://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg',
};
const SONGS = [
{
id: 'one-more-time',
title: 'One More Time',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.DISCOVERY,
},
{
id: 'digital-love',
title: 'Digital Love',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.DISCOVERY,
},
{
id: 'nightvision',
title: 'Nightvision',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.DISCOVERY,
},
{
id: 'something-about-us',
title: 'Something About Us',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.DISCOVERY,
},
{
id: 'veridis-quo',
title: 'Veridis Quo',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.DISCOVERY,
},
{
id: 'make-love',
title: 'Make Love',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.HUMAN_AFTER_ALL,
},
{
id: 'television-rules-the-nation',
title: 'Television Rules the Nation',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.HUMAN_AFTER_ALL,
},
{
id: 'phoenix',
title: 'Phoenix',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.HOMEWORK,
},
{
id: 'revolution-909',
title: 'Revolution 909',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.HOMEWORK,
},
{
id: 'around-the-world',
title: 'Around the World',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.HOMEWORK,
},
{
id: 'within',
title: 'Within',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.RANDOM_ACCESS_MEMORIES,
},
{
id: 'touch',
title: 'Touch (feat. Paul Williams)',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.RANDOM_ACCESS_MEMORIES,
},
{
id: 'beyond',
title: 'Beyond',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.RANDOM_ACCESS_MEMORIES,
},
{
id: 'motherboard',
title: 'Motherboard',
artist: DAFT_PUNK,
cover: ALBUM_COVERS.RANDOM_ACCESS_MEMORIES,
},
];
const SONG_HEIGHT = 70;
const SCROLL_HEIGHT_THRESHOLD = SONG_HEIGHT;
const tabs = ['#FFF', '#00FF00', '#0000FF'];
const Body = ({_this}) => {
const [Colors, Styles] = useStyle(style);
const {width: windowWidth} = useWindowDimensions();
return (
<GestureHandlerRootView style={[Styles.flex1]}>
<ScrollView
horizontal={true}
pagingEnabled
showsHorizontalScrollIndicator={false}
scrollEventThrottle={1}>
{tabs.map((color, index) => {
return (
<View
style={{
width: windowWidth,
height: '100%',
backgroundColor: color,
padding: 20,
}}
key={index}>
{index == 0 && <DraggableColumn />}
</View>
);
})}
</ScrollView>
</GestureHandlerRootView>
);
};
const DraggableColumn = () => {
const positions = useSharedValue(listToObject(SONGS));
const scrollY = useSharedValue(0);
const scrollViewRef = useAnimatedRef();
useAnimatedReaction(
() => scrollY.value,
scrolling => scrollTo(scrollViewRef, 0, scrolling, false),
);
const handleScroll = useAnimatedScrollHandler(event => {
scrollY.value = event.contentOffset.y;
});
return (
<Animated.ScrollView
ref={scrollViewRef}
onScroll={handleScroll}
scrollEventThrottle={16}
showsVerticalScrollIndicator={false}
style={{
flex: 1,
position: 'relative',
backgroundColor: 'white',
}}
contentContainerStyle={{
height: SONGS.length * SONG_HEIGHT,
}}>
{SONGS.map((content, index) => (
<DraggableItem key={index}>
<Item content={content} />
</DraggableItem>
))}
</Animated.ScrollView>
);
};
const DraggableItem = ({children}) => {
const panRef = useRef(null);
const longPressRef = useRef(null);
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const isSelected = useSharedValue(false);
const onLongPressEvent = useAnimatedGestureHandler({
onActive: () => {
console.log('LONGPRESS : active');
isSelected.value = true;
},
onFinish: () => {
console.log('LONGPRESS : finish');
},
});
const onDragEvent = useAnimatedGestureHandler({
onStart: (_event, ctx) => {
console.log('DRAG : start');
ctx.x = translateX.value; // store the current translate value in the context object
ctx.y = translateY.value;
},
onActive: ({translationX, translationY}, ctx) => {
console.log('DRAG : active');
if (isSelected.value) {
translateX.value = ctx.x + translationX; // add the offset on every frame of the animation
translateY.value = ctx.y + translationY;
}
},
onEnd: ({translationY, translationX, velocityX, velocityY}) => {
console.log('DRAG : end');
translateX.value = withSpring(0, {velocity: velocityX});
translateY.value = withSpring(0, {velocity: velocityY});
},
onFinish: () => {
console.log('DRAG : finish');
isSelected.value = false;
},
});
const style = useAnimatedStyle(() => ({
width: '80%',
height: SONG_HEIGHT,
padding: 10,
position: isSelected.value ? 'absolute' : 'relative',
zIndex: isSelected.value ? 99 : 1,
transform: [
{translateX: translateX.value},
{translateY: translateY.value},
{scale: isSelected.value ? withSpring(1.2) : withSpring(1)},
],
}));
return (
<LongPressGestureHandler
ref={longPressRef}
onGestureEvent={onLongPressEvent}
simultaneousHandlers={panRef}
minDurationMs={800}>
<Animated.View>
<PanGestureHandler
activeOffsetX={[-10, 10]}
ref={panRef}
onGestureEvent={onDragEvent}>
<Animated.View style={style}>{children}</Animated.View>
</PanGestureHandler>
</Animated.View>
</LongPressGestureHandler>
);
};
const Item = ({content}) => {
return (
<View
style={{
justifyContent: 'center',
alignItems: 'center',
flex: 1,
backgroundColor: '#DDDDDD',
}}>
<Text>{content.title}</Text>
</View>
);
};
export default Body; |
Beta Was this translation helpful? Give feedback.
-
Probably this thread should be also related somehow to this questions #2316 , #2222 , #1667? Because there is also some issues with responder with nested scroll view. |
Beta Was this translation helpful? Give feedback.
-
Hi, I'm trying to achieve a drag and drop on an element that can begin only by performing a long press. My aim is to wait for the long press handler to activate and immediately allow the pan handler to take over.
My approach seems incorrect, I cannot find any API for doing such things. How would I go about implementing such feature? Thanks a lot.
Beta Was this translation helpful? Give feedback.
All reactions