react-native-gesture-handler 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +4 -0
  2. package/RNGestureHandler.podspec +1 -1
  3. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandler.kt +4 -1
  4. package/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +14 -4
  5. package/android/src/main/java/com/swmansion/gesturehandler/core/NativeViewGestureHandler.kt +0 -13
  6. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt +30 -3
  7. package/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt +4 -0
  8. package/apple/Handlers/RNNativeViewHandler.mm +4 -1
  9. package/apple/RNGestureHandlerButton.h +5 -0
  10. package/apple/RNGestureHandlerButtonComponentView.mm +38 -0
  11. package/apple/RNGestureHandlerDetector.h +0 -2
  12. package/apple/RNGestureHandlerManager.mm +5 -12
  13. package/lib/module/components/Pressable/Pressable.js +6 -2
  14. package/lib/module/components/Pressable/Pressable.js.map +1 -1
  15. package/lib/module/components/Pressable/StateMachine.js +10 -1
  16. package/lib/module/components/Pressable/StateMachine.js.map +1 -1
  17. package/lib/module/components/Pressable/stateDefinitions.js +3 -2
  18. package/lib/module/components/Pressable/stateDefinitions.js.map +1 -1
  19. package/lib/module/components/Pressable/utils.js +32 -1
  20. package/lib/module/components/Pressable/utils.js.map +1 -1
  21. package/lib/module/components/ReanimatedDrawerLayout.js +5 -4
  22. package/lib/module/components/ReanimatedDrawerLayout.js.map +1 -1
  23. package/lib/module/components/utils.js +14 -0
  24. package/lib/module/components/utils.js.map +1 -1
  25. package/lib/module/handlers/gestures/GestureDetector/utils.js +6 -6
  26. package/lib/module/handlers/gestures/GestureDetector/utils.js.map +1 -1
  27. package/lib/module/handlers/gestures/gesture.js +8 -0
  28. package/lib/module/handlers/gestures/gesture.js.map +1 -1
  29. package/lib/module/handlers/gestures/gestureComposition.js +14 -2
  30. package/lib/module/handlers/gestures/gestureComposition.js.map +1 -1
  31. package/lib/module/v3/components/GestureButtons.js +3 -0
  32. package/lib/module/v3/components/GestureButtons.js.map +1 -1
  33. package/lib/module/v3/components/Pressable.js +24 -5
  34. package/lib/module/v3/components/Pressable.js.map +1 -1
  35. package/lib/module/v3/components/Touchable/Touchable.js +3 -0
  36. package/lib/module/v3/components/Touchable/Touchable.js.map +1 -1
  37. package/lib/module/v3/hooks/utils/configUtils.js +3 -3
  38. package/lib/module/v3/hooks/utils/configUtils.js.map +1 -1
  39. package/lib/module/v3/hooks/utils/propsWhiteList.js +1 -1
  40. package/lib/module/v3/hooks/utils/propsWhiteList.js.map +1 -1
  41. package/lib/module/v3/hooks/utils/reanimatedUtils.js +8 -10
  42. package/lib/module/v3/hooks/utils/reanimatedUtils.js.map +1 -1
  43. package/lib/module/web/tools/GestureHandlerWebDelegate.js +5 -4
  44. package/lib/module/web/tools/GestureHandlerWebDelegate.js.map +1 -1
  45. package/lib/typescript/components/Pressable/Pressable.d.ts.map +1 -1
  46. package/lib/typescript/components/Pressable/StateMachine.d.ts +1 -0
  47. package/lib/typescript/components/Pressable/StateMachine.d.ts.map +1 -1
  48. package/lib/typescript/components/Pressable/stateDefinitions.d.ts.map +1 -1
  49. package/lib/typescript/components/Pressable/utils.d.ts +2 -1
  50. package/lib/typescript/components/Pressable/utils.d.ts.map +1 -1
  51. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts +14 -2
  52. package/lib/typescript/components/ReanimatedDrawerLayout.d.ts.map +1 -1
  53. package/lib/typescript/components/utils.d.ts +11 -0
  54. package/lib/typescript/components/utils.d.ts.map +1 -1
  55. package/lib/typescript/handlers/gestures/GestureDetector/utils.d.ts +4 -4
  56. package/lib/typescript/handlers/gestures/GestureDetector/utils.d.ts.map +1 -1
  57. package/lib/typescript/handlers/gestures/gesture.d.ts +4 -0
  58. package/lib/typescript/handlers/gestures/gesture.d.ts.map +1 -1
  59. package/lib/typescript/handlers/gestures/gestureComposition.d.ts.map +1 -1
  60. package/lib/typescript/v3/components/GestureButtons.d.ts.map +1 -1
  61. package/lib/typescript/v3/components/Pressable.d.ts.map +1 -1
  62. package/lib/typescript/v3/components/Touchable/Touchable.d.ts.map +1 -1
  63. package/lib/typescript/v3/hooks/utils/configUtils.d.ts.map +1 -1
  64. package/lib/typescript/v3/hooks/utils/propsWhiteList.d.ts.map +1 -1
  65. package/lib/typescript/v3/hooks/utils/reanimatedUtils.d.ts.map +1 -1
  66. package/lib/typescript/web/tools/GestureHandlerWebDelegate.d.ts.map +1 -1
  67. package/package.json +6 -6
  68. package/scripts/gesture_handler_utils.rb +23 -17
  69. package/src/components/Pressable/Pressable.tsx +9 -1
  70. package/src/components/Pressable/StateMachine.tsx +16 -1
  71. package/src/components/Pressable/stateDefinitions.ts +3 -2
  72. package/src/components/Pressable/utils.ts +37 -0
  73. package/src/components/ReanimatedDrawerLayout.tsx +27 -8
  74. package/src/components/utils.ts +22 -0
  75. package/src/handlers/gestures/GestureDetector/utils.ts +6 -10
  76. package/src/handlers/gestures/gesture.ts +11 -0
  77. package/src/handlers/gestures/gestureComposition.ts +15 -2
  78. package/src/v3/components/GestureButtons.tsx +4 -0
  79. package/src/v3/components/Pressable.tsx +33 -3
  80. package/src/v3/components/Touchable/Touchable.tsx +4 -0
  81. package/src/v3/hooks/utils/configUtils.ts +4 -5
  82. package/src/v3/hooks/utils/propsWhiteList.ts +0 -1
  83. package/src/v3/hooks/utils/reanimatedUtils.ts +9 -10
  84. package/src/web/tools/GestureHandlerWebDelegate.ts +10 -4
@@ -41,7 +41,10 @@ import type {
41
41
  UserSelect,
42
42
  } from '../handlers/gestureHandlerCommon';
43
43
  import { MouseButton } from '../handlers/gestureHandlerCommon';
44
- import { GestureDetector } from '../v3/detectors';
44
+ import {
45
+ InterceptingGestureDetector,
46
+ VirtualGestureDetector,
47
+ } from '../v3/detectors';
45
48
  import type { PanGestureActiveEvent } from '../v3/hooks/gestures';
46
49
  import { usePanGesture, useTapGesture } from '../v3/hooks/gestures';
47
50
  import type { WithSharedValue } from '../v3/types';
@@ -185,15 +188,28 @@ export type DrawerLayoutProps = {
185
188
  overlayColor?: string;
186
189
 
187
190
  /**
188
- * Style wrapping the content.
191
+ * Style applied to the container wrapping the content view (the `children`)
192
+ * and the background overlay.
189
193
  */
190
194
  contentContainerStyle?: StyleProp<ViewStyle>;
191
195
 
192
196
  /**
193
- * Style wrapping the drawer.
197
+ * Style applied to the container wrapping the drawer (the view returned by
198
+ * `renderNavigationView`).
194
199
  */
195
200
  drawerContainerStyle?: StyleProp<ViewStyle>;
196
201
 
202
+ /**
203
+ * Style applied to the outermost container that wraps both the content view
204
+ * and the drawer.
205
+ */
206
+ rootContainerStyle?: StyleProp<ViewStyle>;
207
+
208
+ /**
209
+ * Called while the drawer is moving or animating, with a `position`
210
+ * parameter indicating the progress of the opening/closing animation.
211
+ * It equals `0` when the drawer is closed and `1` when it is fully opened.
212
+ */
197
213
  onDrawerSlide?: (position: number) => void;
198
214
 
199
215
  // Implicit `children` prop has been removed in @types/react^18.0.
@@ -283,6 +299,7 @@ const DrawerLayout = function DrawerLayout(
283
299
  drawerType = defaultProps.drawerType,
284
300
  drawerBackgroundColor,
285
301
  drawerContainerStyle,
302
+ rootContainerStyle,
286
303
  contentContainerStyle,
287
304
  minSwipeDistance = defaultProps.minSwipeDistance,
288
305
  edgeWidth = defaultProps.edgeWidth,
@@ -657,12 +674,14 @@ const DrawerLayout = function DrawerLayout(
657
674
  );
658
675
 
659
676
  return (
660
- <GestureDetector
677
+ <InterceptingGestureDetector
661
678
  gesture={panGesture}
662
679
  userSelect={userSelect}
663
680
  enableContextMenu={enableContextMenu}>
664
- <Animated.View style={styles.main} onLayout={handleContainerLayout}>
665
- <GestureDetector
681
+ <Animated.View
682
+ style={[styles.main, rootContainerStyle]}
683
+ onLayout={handleContainerLayout}>
684
+ <VirtualGestureDetector
666
685
  gesture={overlayDismissGesture}
667
686
  userSelect={userSelect}>
668
687
  <Animated.View
@@ -680,7 +699,7 @@ const DrawerLayout = function DrawerLayout(
680
699
  style={[styles.overlay, overlayAnimatedStyle]}
681
700
  />
682
701
  </Animated.View>
683
- </GestureDetector>
702
+ </VirtualGestureDetector>
684
703
  <Animated.View
685
704
  pointerEvents="box-none"
686
705
  animatedProps={drawerAnimatedProps}
@@ -694,7 +713,7 @@ const DrawerLayout = function DrawerLayout(
694
713
  </Animated.View>
695
714
  </Animated.View>
696
715
  </Animated.View>
697
- </GestureDetector>
716
+ </InterceptingGestureDetector>
698
717
  );
699
718
  };
700
719
 
@@ -1,5 +1,27 @@
1
+ import { Platform } from 'react-native';
2
+
1
3
  import type { BaseGesture, GestureRef } from '../handlers/gestures/gesture';
2
4
 
5
+ type TVProps = {
6
+ focusable?: boolean | undefined;
7
+ isTVSelectable?: boolean | undefined;
8
+ };
9
+
10
+ /**
11
+ * Gesture Handler buttons render the native button component directly, bypassing RN's `<View>` —
12
+ * the only place the `focusable` prop is translated into `isTVSelectable`, which actually drives
13
+ * tvOS focusability.
14
+ */
15
+ export function getTVProps(props: TVProps): TVProps {
16
+ if (!Platform.isTV) {
17
+ return {};
18
+ }
19
+
20
+ return {
21
+ isTVSelectable: props.focusable ?? props.isTVSelectable ?? true,
22
+ };
23
+ }
24
+
3
25
  export type RelationPropName =
4
26
  | 'simultaneousWithExternalGesture'
5
27
  | 'requireExternalGestureToFail'
@@ -65,20 +65,16 @@ function extractValidHandlerTags(interactionGroup: GestureRef[] | undefined) {
65
65
  }
66
66
 
67
67
  export function extractGestureRelations(gesture: GestureType) {
68
- gesture.config.requireToFail = extractValidHandlerTags(
69
- gesture.config.requireToFail
70
- );
71
- gesture.config.simultaneousWith = extractValidHandlerTags(
68
+ const requireToFail = extractValidHandlerTags(gesture.config.requireToFail);
69
+ const simultaneousWith = extractValidHandlerTags(
72
70
  gesture.config.simultaneousWith
73
71
  );
74
- gesture.config.blocksHandlers = extractValidHandlerTags(
75
- gesture.config.blocksHandlers
76
- );
72
+ const blocksHandlers = extractValidHandlerTags(gesture.config.blocksHandlers);
77
73
 
78
74
  return {
79
- waitFor: gesture.config.requireToFail,
80
- simultaneousHandlers: gesture.config.simultaneousWith,
81
- blocksHandlers: gesture.config.blocksHandlers,
75
+ waitFor: requireToFail,
76
+ simultaneousHandlers: simultaneousWith,
77
+ blocksHandlers: blocksHandlers,
82
78
  };
83
79
  }
84
80
 
@@ -137,6 +137,17 @@ export abstract class BaseGesture<
137
137
  public handlerTag = -1;
138
138
  public handlerName = '';
139
139
  public config: BaseGestureConfig = {};
140
+ // Snapshot of the relations defined directly on this gesture (e.g. via
141
+ // `simultaneousWithExternalGesture`), captured before any composition extends
142
+ // them. Composition rebuilds the relation config from this snapshot on every
143
+ // `prepare`, so repeated renders don't accumulate references to gestures from
144
+ // previous renders (memory leak, see #3763), while keeping the original
145
+ // references so relations stay re-resolvable after a remount, such as a
146
+ // `react-freeze` unfreeze (see #4238).
147
+ public relationsSnapshot?: {
148
+ simultaneousWith: GestureRef[] | undefined;
149
+ requireToFail: GestureRef[] | undefined;
150
+ };
140
151
  public handlers: HandlerCallbacks<EventPayloadT> = {
141
152
  gestureId: -1,
142
153
  handlerTag: -1,
@@ -31,16 +31,29 @@ export class ComposedGesture extends Gesture {
31
31
  requireGesturesToFail: GestureType[]
32
32
  ) {
33
33
  if (gesture instanceof BaseGesture) {
34
+ // Capture the relations defined directly on the gesture before composition
35
+ // extends them, then always rebuild from that snapshot. Otherwise, when the
36
+ // gesture is stable (e.g. wrapped in `useMemo`) but the composition is
37
+ // recreated on every render, the relations would keep accumulating
38
+ // references to gestures from previous renders, leaking memory (see #3763).
39
+ // We keep the original references (instead of collapsing them to handler
40
+ // tags) so relations can still be re-resolved after a remount, such as a
41
+ // `react-freeze` unfreeze (see #4238).
42
+ gesture.relationsSnapshot ??= {
43
+ simultaneousWith: gesture.config.simultaneousWith,
44
+ requireToFail: gesture.config.requireToFail,
45
+ };
46
+
34
47
  const newConfig = { ...gesture.config };
35
48
 
36
49
  // No need to extend `blocksHandlers` here, because it's not changed in composition.
37
50
  // The same effect is achieved by reversing the order of 2 gestures in `Exclusive`
38
51
  newConfig.simultaneousWith = extendRelation(
39
- newConfig.simultaneousWith,
52
+ gesture.relationsSnapshot.simultaneousWith,
40
53
  simultaneousGestures
41
54
  );
42
55
  newConfig.requireToFail = extendRelation(
43
- newConfig.requireToFail,
56
+ gesture.relationsSnapshot.requireToFail,
44
57
  requireGesturesToFail
45
58
  );
46
59
 
@@ -2,6 +2,7 @@ import React, { useRef } from 'react';
2
2
  import { Animated, Platform, StyleSheet } from 'react-native';
3
3
 
4
4
  import GestureHandlerButton from '../../components/GestureHandlerButton';
5
+ import { getTVProps } from '../../components/utils';
5
6
  import createNativeWrapper from '../createNativeWrapper';
6
7
  import type { NativeHandlerData } from '../hooks/gestures/native/NativeTypes';
7
8
  import type { GestureEndEvent, GestureEvent } from '../types';
@@ -95,10 +96,13 @@ export const BaseButton = (props: BaseButtonProps) => {
95
96
  props.onFinalize?.(e);
96
97
  };
97
98
 
99
+ const tvProps = getTVProps(rest);
100
+
98
101
  return (
99
102
  <RawButton
100
103
  style={[style, Platform.OS === 'ios' && { cursor: undefined }]}
101
104
  {...rest}
105
+ {...tvProps}
102
106
  onBegin={onBegin}
103
107
  onActivate={onActivate}
104
108
  onDeactivate={onDeactivate}
@@ -29,7 +29,9 @@ import {
29
29
  gestureTouchToPressableEvent,
30
30
  isTouchWithinInset,
31
31
  numberAsInset,
32
+ viewCenterToPressableEvent,
32
33
  } from '../../components/Pressable/utils';
34
+ import { getTVProps } from '../../components/utils';
33
35
  import { PressabilityDebugView } from '../../handlers/PressabilityDebugView';
34
36
  import { useIsScreenReaderEnabled } from '../../useIsScreenReaderEnabled';
35
37
  import { INT32_MAX, isTestEnv } from '../../utils';
@@ -151,8 +153,9 @@ const Pressable = (props: PressableProps) => {
151
153
  }, [cancelDelayedPress, cancelLongPress]);
152
154
 
153
155
  const handlePressIn = useCallback(
154
- (event: PressableEvent) => {
156
+ (event: PressableEvent, skipBoundsCheck = false) => {
155
157
  if (
158
+ !skipBoundsCheck &&
156
159
  !isTouchWithinInset(
157
160
  dimensions.current,
158
161
  normalizedHitSlop,
@@ -305,11 +308,25 @@ const Pressable = (props: PressableProps) => {
305
308
  }
306
309
  },
307
310
  onBegin: () => {
311
+ if (Platform.isTV) {
312
+ // tvOS drives this native gesture from the focus-engine Select press.
313
+ // The press state machine is touch-based and never
314
+ // receives LONG_PRESS_TOUCHES_DOWN here, so bypass it and drive the press handlers directly.
315
+ // A focus-driven press has no coordinates, so skip the hit-slop bounds check entirely.
316
+ handlePressIn(viewCenterToPressableEvent(dimensions.current), true);
317
+ return;
318
+ }
319
+ if (Platform.OS === 'android' && isScreenReaderEnabled) {
320
+ stateMachine.handleEvent(
321
+ StateMachineEvent.NATIVE_BEGIN,
322
+ viewCenterToPressableEvent(dimensions.current)
323
+ );
324
+ return;
325
+ }
308
326
  stateMachine.handleEvent(StateMachineEvent.NATIVE_BEGIN);
309
327
  },
310
328
  onActivate: () => {
311
- if (Platform.OS !== 'android') {
312
- // Native.onActivate is broken with Android + hitSlop
329
+ if (!Platform.isTV && Platform.OS !== 'android') {
313
330
  stateMachine.handleEvent(StateMachineEvent.NATIVE_START);
314
331
  }
315
332
  },
@@ -319,6 +336,16 @@ const Pressable = (props: PressableProps) => {
319
336
  if (Platform.OS === 'web') {
320
337
  return;
321
338
  }
339
+
340
+ if (Platform.isTV) {
341
+ handlePressOut(
342
+ viewCenterToPressableEvent(dimensions.current),
343
+ !event.canceled
344
+ );
345
+ handleFinalize();
346
+ return;
347
+ }
348
+
322
349
  stateMachine.handleEvent(
323
350
  event.canceled ? StateMachineEvent.CANCEL : StateMachineEvent.FINALIZE
324
351
  );
@@ -365,10 +392,13 @@ const Pressable = (props: PressableProps) => {
365
392
  [onLayout]
366
393
  );
367
394
 
395
+ const tvProps = getTVProps(remainingProps);
396
+
368
397
  return (
369
398
  <GestureDetector gesture={gesture}>
370
399
  <PureNativeButton
371
400
  {...remainingProps}
401
+ {...tvProps}
372
402
  onLayout={setDimensions}
373
403
  accessible={accessible !== false}
374
404
  hitSlop={appliedHitSlop}
@@ -2,6 +2,7 @@ import React, { useCallback, useRef } from 'react';
2
2
  import { Platform } from 'react-native';
3
3
 
4
4
  import GestureHandlerButton from '../../../components/GestureHandlerButton';
5
+ import { getTVProps } from '../../../components/utils';
5
6
  import { NativeDetector } from '../../detectors/NativeDetector';
6
7
  import { useNativeGesture } from '../../hooks';
7
8
  import type {
@@ -208,10 +209,13 @@ export const Touchable = (props: TouchableProps) => {
208
209
  }
209
210
  : TRANSPARENT_RIPPLE;
210
211
 
212
+ const tvProps = getTVProps(rest);
213
+
211
214
  return (
212
215
  <NativeDetector gesture={nativeGesture}>
213
216
  <GestureHandlerButton
214
217
  {...rest}
218
+ {...tvProps}
215
219
  {...rippleProps}
216
220
  {...resolvedDurations}
217
221
  ref={ref ?? null}
@@ -21,8 +21,6 @@ export function resolveInternalConfigProps<
21
21
  THandlerData,
22
22
  TExtendedHandlerData extends THandlerData,
23
23
  >(config: BaseGestureConfig<TConfig, THandlerData, TExtendedHandlerData>) {
24
- const runOnJS = maybeUnpackValue(config.runOnJS);
25
-
26
24
  if (
27
25
  __DEV__ &&
28
26
  isNativeAnimatedEvent(config.onUpdate) &&
@@ -63,8 +61,6 @@ export function resolveInternalConfigProps<
63
61
  hasWorkletEventHandlers(config) &&
64
62
  !config.dispatchesAnimatedEvents;
65
63
  config.needsPointerData = shouldHandleTouchEvents(config);
66
- config.dispatchesReanimatedEvents =
67
- config.shouldUseReanimatedDetector && !runOnJS;
68
64
  }
69
65
 
70
66
  export function prepareConfigForNativeSide<
@@ -80,7 +76,10 @@ export function prepareConfigForNativeSide<
80
76
  TConfig,
81
77
  THandlerData,
82
78
  TExtendedHandlerData
83
- > = {};
79
+ > = {
80
+ dispatchesReanimatedEvents:
81
+ config.shouldUseReanimatedDetector && !maybeUnpackValue(config.runOnJS),
82
+ };
84
83
  const handlerPropsWhiteList =
85
84
  PropsWhiteLists.get(handlerType) ?? EMPTY_WHITE_LIST;
86
85
 
@@ -42,7 +42,6 @@ export const allowedNativeProps = new Set<
42
42
  'userSelect',
43
43
  'enableContextMenu',
44
44
  'touchAction',
45
- 'dispatchesReanimatedEvents',
46
45
  'dispatchesAnimatedEvents',
47
46
  'needsPointerData',
48
47
  ]);
@@ -38,6 +38,7 @@ export function bindSharedValues<
38
38
  }
39
39
 
40
40
  const baseListenerId = handlerTag + SHARED_VALUE_OFFSET;
41
+ const { shouldUseReanimatedDetector } = config;
41
42
 
42
43
  const attachListener = (sharedValue: SharedValue, configKey: string) => {
43
44
  'worklet';
@@ -45,16 +46,14 @@ export function bindSharedValues<
45
46
  const listenerId = baseListenerId + keyHash;
46
47
 
47
48
  sharedValue.addListener(listenerId, (value) => {
48
- if (configKey === 'runOnJS') {
49
- config.dispatchesReanimatedEvents =
50
- config.shouldUseReanimatedDetector && !value;
51
-
52
- updateGestureHandlerConfig(handlerTag, {
53
- dispatchesReanimatedEvents: config.dispatchesReanimatedEvents,
54
- });
55
- } else {
56
- updateGestureHandlerConfig(handlerTag, { [configKey]: value });
57
- }
49
+ updateGestureHandlerConfig(
50
+ handlerTag,
51
+ configKey === 'runOnJS'
52
+ ? {
53
+ dispatchesReanimatedEvents: shouldUseReanimatedDetector && !value,
54
+ }
55
+ : { [configKey]: value }
56
+ );
58
57
  });
59
58
  };
60
59
 
@@ -141,8 +141,14 @@ export class GestureHandlerWebDelegate
141
141
  throw new Error(tagMessage('Cannot convert coords on a null view'));
142
142
  }
143
143
 
144
- const rect = getEffectiveBoundingRect(this.view);
145
- const transform = getComputedStyle(this.view).transform;
144
+ const localView =
145
+ this.gestureHandler.usesNativeOrVirtualDetector() &&
146
+ this.view.style.display === 'contents'
147
+ ? (this.view.children[0] as HTMLElement)
148
+ : this.view;
149
+
150
+ const rect = getEffectiveBoundingRect(localView);
151
+ const transform = getComputedStyle(localView).transform;
146
152
  const matrix =
147
153
  transform && transform !== 'none'
148
154
  ? new DOMMatrix(transform)
@@ -164,8 +170,8 @@ export class GestureHandlerWebDelegate
164
170
  const localOffset = inverse.transformPoint(new DOMPoint(dx, dy));
165
171
 
166
172
  // Add back the local center (untransformed dimensions)
167
- const localCenterX = this.view.offsetWidth / 2;
168
- const localCenterY = this.view.offsetHeight / 2;
173
+ const localCenterX = localView.offsetWidth / 2;
174
+ const localCenterY = localView.offsetHeight / 2;
169
175
 
170
176
  return {
171
177
  x: localCenterX + localOffset.x,