Skip to content

Commit 3a829f7

Browse files
committed
Correct Pressability region from touch location
1 parent ef683f7 commit 3a829f7

2 files changed

Lines changed: 84 additions & 6 deletions

File tree

packages/react-native/Libraries/Pressability/Pressability.js

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ export default class Pressability {
385385
top: number,
386386
}> = null;
387387
_touchActivatePosition: ?Readonly<{
388+
locationX: number,
389+
locationY: number,
388390
pageX: number,
389391
pageY: number,
390392
}>;
@@ -721,6 +723,7 @@ export default class Pressability {
721723
!isActivationSignal(prevState) && isActivationSignal(nextState);
722724

723725
if (isInitialTransition || isActivationTransition) {
726+
this._recordTouchActivatePosition(event);
724727
this._measureResponderRegion();
725728
}
726729

@@ -764,14 +767,18 @@ export default class Pressability {
764767

765768
_activate(event: GestureResponderEvent): void {
766769
const {onPressIn} = this._config;
767-
const {pageX, pageY} = getTouchFromPressEvent(event);
768-
this._touchActivatePosition = {pageX, pageY};
770+
this._recordTouchActivatePosition(event);
769771
this._touchActivateTime = Date.now();
770772
if (onPressIn != null) {
771773
onPressIn(event);
772774
}
773775
}
774776

777+
_recordTouchActivatePosition(event: GestureResponderEvent): void {
778+
const {locationX, locationY, pageX, pageY} = getTouchFromPressEvent(event);
779+
this._touchActivatePosition = {locationX, locationY, pageX, pageY};
780+
}
781+
775782
_deactivate(event: GestureResponderEvent): void {
776783
const {onPressOut} = this._config;
777784
if (onPressOut != null) {
@@ -820,16 +827,42 @@ export default class Pressability {
820827
if (!left && !top && !width && !height && !pageX && !pageY) {
821828
return;
822829
}
823-
this._responderRegion = {
830+
const responderRegion = {
824831
bottom: pageY + height,
825832
left: pageX,
826833
right: pageX + width,
827834
top: pageY,
828835
};
836+
837+
const touch = this._touchActivatePosition;
838+
if (
839+
touch != null &&
840+
!this._isTouchWithinResponderRegion(touch, responderRegion)
841+
) {
842+
const correctedLeft = touch.pageX - touch.locationX;
843+
const correctedTop = touch.pageY - touch.locationY;
844+
const correctedRegion = {
845+
bottom: correctedTop + height,
846+
left: correctedLeft,
847+
right: correctedLeft + width,
848+
top: correctedTop,
849+
};
850+
851+
if (this._isTouchWithinResponderRegion(touch, correctedRegion)) {
852+
this._responderRegion = correctedRegion;
853+
return;
854+
}
855+
}
856+
857+
this._responderRegion = responderRegion;
829858
};
830859

831860
_isTouchWithinResponderRegion(
832-
touch: GestureResponderEvent['nativeEvent'],
861+
touch: Readonly<{
862+
pageX: number,
863+
pageY: number,
864+
...
865+
}>,
833866
responderRegion: Readonly<{
834867
bottom: number,
835868
left: number,

packages/react-native/Libraries/Pressability/__tests__/Pressability-test.js

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,26 +171,35 @@ const createMockPressEvent = (
171171
registrationName: string,
172172
pageX: number,
173173
pageY: number,
174+
locationX?: number,
175+
locationY?: number,
174176
}>,
175177
): GestureResponderEvent => {
176178
let registrationName = '';
177179
let pageX = 0;
178180
let pageY = 0;
181+
let locationX = 0;
182+
let locationY = 0;
179183

180184
if (typeof nameOrOverrides === 'string') {
181185
registrationName = nameOrOverrides;
182186
} else if (typeof nameOrOverrides === 'object' || nameOrOverrides != null) {
183187
registrationName = nameOrOverrides.registrationName;
184188
pageX = nameOrOverrides.pageX;
185189
pageY = nameOrOverrides.pageY;
190+
locationX = nameOrOverrides.locationX ?? pageX;
191+
locationY = nameOrOverrides.locationY ?? pageY;
192+
} else {
193+
locationX = pageX;
194+
locationY = pageY;
186195
}
187196

188197
const nativeEvent = {
189198
changedTouches: [] as Array<GestureResponderEvent['nativeEvent']>,
190199
force: 1,
191200
identifier: 42,
192-
locationX: pageX,
193-
locationY: pageY,
201+
locationX,
202+
locationY,
194203
pageX,
195204
pageY,
196205
target: 42,
@@ -980,6 +989,42 @@ describe('Pressability', () => {
980989
jest.advanceTimersByTime(630); // 1000 - 500 (onPressIn activation, already advanced before) + DEFAULT_MIN_PRESS_DURATION
981990
expect(config.onPressOut).toBeCalled();
982991
});
992+
993+
it('uses touch location when measured region is shifted from visual position', () => {
994+
getMock(UIManager.measure).mockImplementation((id, fn) => {
995+
fn(0, 500, mockRegion.width, mockRegion.height, 0, 500);
996+
});
997+
const {config, handlers} = createMockPressability({
998+
delayPressIn: 0,
999+
});
1000+
1001+
handlers.onStartShouldSetResponder();
1002+
handlers.onResponderGrant(
1003+
createMockPressEvent({
1004+
registrationName: 'onResponderGrant',
1005+
pageX: 25,
1006+
pageY: 25,
1007+
locationX: 25,
1008+
locationY: 25,
1009+
}),
1010+
);
1011+
1012+
expect(UIManager.measure).toBeCalled();
1013+
1014+
handlers.onResponderMove(
1015+
createMockPressEvent({
1016+
registrationName: 'onResponderMove',
1017+
pageX: 25,
1018+
pageY: 25,
1019+
locationX: 25,
1020+
locationY: 25,
1021+
}),
1022+
);
1023+
handlers.onResponderRelease(createMockPressEvent('onResponderRelease'));
1024+
1025+
expect(config.onPressIn).toBeCalled();
1026+
expect(config.onPress).toBeCalled();
1027+
});
9831028
});
9841029
});
9851030

0 commit comments

Comments
 (0)