Skip to content

Commit 3da9a68

Browse files
m-bertj-piasecki
andauthored
[docs] Gesture detectors (#3894)
## Description In this PR, page dedicated to gesture detectors is rewritten. ## Test plan Read docs 🤓 --------- Co-authored-by: Jakub Piasecki <jakub.piasecki@swmansion.com>
1 parent d548122 commit 3da9a68

18 files changed

Lines changed: 666 additions & 133 deletions
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
id: animated-interactions
3+
title: Integration with Animated
4+
sidebar_label: Integration with Animated
5+
sidebar_position: 5
6+
---
7+
8+
import CollapsibleCode from '@site/src/components/CollapsibleCode';
9+
10+
Using hook API allows for smooth integration with the [Animated API](https://reactnative.dev/docs/animated) by allowing for passing an `Animated.event` as the argument to the `onUpdate` callback. The event mapping of `Animated.event` depends on the `useNativeDriver` property.
11+
12+
When using Animated API, remember to set `useAnimated` property to `true`.
13+
14+
:::danger Mixing Reanimated and Animated
15+
It is not possible to mix `Reanimated` and `Animated` within any of the [gesture detectors](/docs/fundamentals/gesture-detectors).
16+
:::
17+
18+
<CollapsibleCode
19+
label="Show full example"
20+
expandedLabel="Hide full example"
21+
lineBounds={[8, 33]}
22+
src={`
23+
import * as React from 'react';
24+
import { Animated, StyleSheet, useAnimatedValue } from 'react-native';
25+
import {
26+
GestureHandlerRootView,
27+
GestureDetector,
28+
usePanGesture,
29+
} from 'react-native-gesture-handler';
30+
31+
export default function App() {
32+
const value = useAnimatedValue(0);
33+
34+
const event = Animated.event(
35+
[{ nativeEvent: { handlerData: { translationX: value } } }],
36+
{
37+
useNativeDriver: true,
38+
}
39+
);
40+
41+
const gesture = usePanGesture({
42+
// highlight-next-line
43+
onUpdate: event,
44+
// highlight-next-line
45+
useAnimated: true,
46+
});
47+
48+
return (
49+
<GestureHandlerRootView>
50+
<GestureDetector gesture={gesture}>
51+
<Animated.View
52+
style={[styles.box, { transform: [{ translateX: value }] }]}
53+
/>
54+
</GestureDetector>
55+
</GestureHandlerRootView>
56+
);
57+
}
58+
59+
const styles = StyleSheet.create({
60+
box: {
61+
width: 150,
62+
height: 150,
63+
backgroundColor: '#b58df1',
64+
},
65+
});
66+
`}/>
67+
68+
## useNativeDriver
69+
70+
When using `Animated.event` with `useNativeDriver` set to `false`, it is required to set [`disableReanimated`](/docs/fundamentals/reanimated-interactions#disablereanimated) to `true` in the gesture configuration.
71+
72+
Mapping of `Animated.event` depends on the value of `useNativeDriver` property. When set to `true`, event data can be accessed through `nativeEvent.handlerData` property:
73+
74+
```jsx
75+
const value = useAnimatedValue(0);
76+
77+
const event = Animated.event(
78+
[{ nativeEvent: { handlerData: { /* translationX: value, ... */ } } }],
79+
{ useNativeDriver: true }
80+
);
81+
```
82+
83+
In case of `useNativeDriver` set to `false`, event data is accessed directly:
84+
85+
```jsx
86+
const value = useAnimatedValue(0);
87+
88+
const event = Animated.event(
89+
[ { /* translationX: value, ... */ } ],
90+
{ useNativeDriver: false }
91+
);
92+
```
93+
94+
## Usage with VirtualGestureDetector
95+
96+
Using `Animated.event` with [`VirtualGestureDetector`](/docs/fundamentals/gesture-detectors#virtualgesturedetector) is possible only when `useNativeDriver` is set to `false`.
97+
98+
<CollapsibleCode
99+
label="Show full example"
100+
expandedLabel="Hide full example"
101+
lineBounds={[8, 33]}
102+
src={`
103+
import React from 'react';
104+
import { View, StyleSheet, Animated, useAnimatedValue } from 'react-native';
105+
import {
106+
GestureHandlerRootView,
107+
InterceptingGestureDetector,
108+
usePanGesture,
109+
VirtualGestureDetector,
110+
} from 'react-native-gesture-handler';
111+
112+
export default function App() {
113+
const value = useAnimatedValue(0);
114+
const event = Animated.event([{ translationX: value }], {
115+
useNativeDriver: false,
116+
});
117+
118+
const panGesture = usePanGesture({
119+
onUpdate: event,
120+
disableReanimated: true,
121+
});
122+
123+
return (
124+
<GestureHandlerRootView style={styles.container}>
125+
<InterceptingGestureDetector>
126+
<View style={styles.outerBox}>
127+
<VirtualGestureDetector gesture={panGesture}>
128+
<Animated.View
129+
style={[styles.innerBox, { transform: [{ translateX: value }] }]}
130+
/>
131+
</VirtualGestureDetector>
132+
</View>
133+
</InterceptingGestureDetector>
134+
</GestureHandlerRootView>
135+
);
136+
}
137+
138+
const styles = StyleSheet.create({
139+
container: {
140+
flex: 1,
141+
alignItems: 'center',
142+
justifyContent: 'center',
143+
},
144+
outerBox: {
145+
backgroundColor: '#b58df1',
146+
width: 150,
147+
height: 150,
148+
},
149+
innerBox: {
150+
width: 100,
151+
height: 100,
152+
backgroundColor: 'blue',
153+
},
154+
});
155+
`}/>

packages/docs-gesture-handler/docs/fundamentals/gesture-composition.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
id: gesture-composition
33
title: Gesture composition & interactions
44
sidebar_label: Gesture composition & interactions
5-
sidebar_position: 10
5+
sidebar_position: 6
66
---
77

88
RNGH3 simplifies gesture interaction through dedicated composition hooks and configuration properties. To choose the right approach, simply ask: Are all the gestures attached to the same component?
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
---
2+
id: gesture-detectors
3+
title: Gesture Detectors
4+
sidebar_label: Gesture detectors
5+
sidebar_position: 3
6+
---
7+
8+
import CollapsibleCode from '@site/src/components/CollapsibleCode';
9+
10+
11+
## Gesture Detector
12+
13+
The `GestureDetector` is a key component of `react-native-gesture-handler`. It supports gestures created either using the hooks API or the builder pattern. Additionally, it allows for the recognition of multiple gestures through [gesture composition](/docs/fundamentals/gesture-composition). `GestureDetector` interacts closely with [`Reanimated`](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/). For more details, refer to the [Integration with Reanimated](/docs/fundamentals/reanimated-interactions) section.
14+
15+
When using hook API, you can also integrate it directly with the [Animated API](https://reactnative.dev/docs/animated). More on that can be found in [Integreation with Animated](/docs/fundamentals/animated-interactions) section.
16+
17+
:::danger
18+
19+
#### Nesting Gesture Detectors
20+
21+
Because `GestureDetector` supports both the hook API and the builder pattern, it is important to avoid nesting detectors that use different APIs, as this can result in undefined behavior.
22+
23+
#### Reusing Gestures
24+
Using the same instance of a gesture across multiple Gesture Detectors may result in undefined behavior.
25+
:::
26+
27+
```js
28+
import { GestureDetector, useTapGesture } from 'react-native-gesture-handler';
29+
30+
export default function App() {
31+
const tap = useTapGesture({
32+
onDeactivate: () => {
33+
console.log('Tap!');
34+
},
35+
});
36+
37+
return (
38+
<GestureHandlerRootView>
39+
// highlight-next-line
40+
<GestureDetector gesture={tap}>
41+
<Animated.View />
42+
// highlight-next-line
43+
</GestureDetector>
44+
</GestureHandlerRootView>
45+
);
46+
}
47+
```
48+
49+
## Virtual Detectors
50+
51+
Since RNGH3, `GestureDetector` is a standalone host component. Depending on the view hierarchy, this can occasionally disrupt interactions between specific components. To resolve this, use `InterceptingGestureDetector` in combination with `VirtualNativeDetector`.
52+
53+
### InterceptingGestureDetector
54+
55+
`InterceptingGestureDetector` functions similarly to a `GestureDetector`, but it can also act as a proxy for `VirtualGestureDetector` within its component subtree. Because it can be used solely to establish the context for virtual detectors, the [`gesture`](#gesture) property is optional.
56+
57+
### VirtualGestureDetector
58+
59+
`VirtualGestureDetector` is similar to the `GestureDetector` from RNGH2. Because it is not a host component, it does not interfere with the host view hierarchy. This allows you to attach gestures without disrupting functionality that depends on it.
60+
61+
### Known use cases
62+
63+
Here are some of the most common use cases for virtual gesture detectors.
64+
65+
#### SVG
66+
67+
You can combine `VirtualGestureDetector` with `react-native-svg` to add gesture handling to individual SVG elements.
68+
69+
<CollapsibleCode
70+
label="Show full example"
71+
expandedLabel="Hide full example"
72+
lineBounds={[10, 45]}
73+
src={`
74+
import React from 'react';
75+
import { View, StyleSheet } from 'react-native';
76+
import {
77+
GestureHandlerRootView,
78+
InterceptingGestureDetector,
79+
useTapGesture,
80+
VirtualGestureDetector,
81+
} from 'react-native-gesture-handler';
82+
import Svg, { Circle } from 'react-native-svg';
83+
84+
export default function App() {
85+
const outerTap = useTapGesture({
86+
onDeactivate: () => {
87+
console.log('Box tapped!');
88+
},
89+
});
90+
const innerTap = useTapGesture({
91+
onDeactivate: () => {
92+
console.log('Circle tapped!');
93+
},
94+
});
95+
96+
return (
97+
<GestureHandlerRootView style={styles.container}>
98+
// highlight-next-line
99+
<InterceptingGestureDetector gesture={outerTap}>
100+
<View style={styles.box}>
101+
<Svg height="250" width="250">
102+
// highlight-next-line
103+
<VirtualGestureDetector gesture={innerTap}>
104+
<Circle
105+
cx="125"
106+
cy="125"
107+
r="125"
108+
fill="#001A72"
109+
onPress={() => {}}
110+
/>
111+
// highlight-next-line
112+
</VirtualGestureDetector>
113+
</Svg>
114+
</View>
115+
// highlight-next-line
116+
</InterceptingGestureDetector>
117+
</GestureHandlerRootView>
118+
);
119+
}
120+
121+
const styles = StyleSheet.create({
122+
container: {
123+
flex: 1,
124+
alignItems: 'center',
125+
justifyContent: 'center',
126+
},
127+
box: {
128+
backgroundColor: '#b58df1',
129+
},
130+
});
131+
`}/>
132+
133+
134+
#### Text
135+
136+
You can use `VirtualGestureDetector` to add gesture handling to specific parts of a `Text` component.
137+
138+
<CollapsibleCode
139+
label="Show full example"
140+
expandedLabel="Hide full example"
141+
lineBounds={[8, 37]}
142+
src={`
143+
import * as React from 'react';
144+
import { StyleSheet, Text } from 'react-native';
145+
import {
146+
GestureHandlerRootView,
147+
InterceptingGestureDetector,
148+
VirtualGestureDetector,
149+
useTapGesture,
150+
} from 'react-native-gesture-handler';
151+
152+
export default function App() {
153+
const outerTap = useTapGesture({
154+
onDeactivate: () => {
155+
console.log('Tapped on text!');
156+
},
157+
});
158+
159+
const nestedTap = useTapGesture({
160+
onDeactivate: () => {
161+
console.log('Tapped on nested part!');
162+
},
163+
});
164+
165+
return (
166+
<GestureHandlerRootView style={styles.container}>
167+
<InterceptingGestureDetector gesture={outerTap}>
168+
<Text style={{ fontSize: 18, textAlign: 'center' }}>
169+
Nested text
170+
<VirtualGestureDetector gesture={nestedTap}>
171+
<Text style={{ fontSize: 24, color: '#001A72' }}>
172+
try tapping on this part.
173+
</Text>
174+
</VirtualGestureDetector>
175+
This part is not special :c
176+
</Text>
177+
</InterceptingGestureDetector>
178+
</GestureHandlerRootView>
179+
);
180+
}
181+
182+
const styles = StyleSheet.create({
183+
container: {
184+
flex: 1,
185+
alignItems: 'center',
186+
justifyContent: 'space-around',
187+
},
188+
});
189+
`}/>
190+
191+
192+
## Properties
193+
194+
### gesture
195+
196+
```ts
197+
gesture: SingleGesture | ComposedGesture;
198+
```
199+
200+
A gesture object containing the configuration and callbacks. Can be any of the base gestures or any [`ComposedGesture`](/docs/fundamentals/gesture-composition).
201+
202+
### userSelect (Web only)
203+
204+
```ts
205+
userSelect: 'none' | 'auto' | 'text';
206+
```
207+
208+
This parameter allows to specify which `userSelect` property should be applied to underlying view. Default value is set to `"none"`.
209+
210+
### touchAction (Web only)
211+
212+
```ts
213+
userSelect: TouchAction;
214+
```
215+
216+
This parameter allows to specify which `touchAction` property should be applied to underlying view. Supports all CSS [touch-action](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/touch-action) values. Default value is set to `"none"`.
217+
218+
### enableContextMenu (Web only)
219+
220+
```ts
221+
enableContextMenu: boolean;
222+
```
223+
224+
Specifies whether context menu should be enabled after clicking on underlying view with right mouse button. Default value is set to `false`.

0 commit comments

Comments
 (0)