|
| 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