From f393b8e9bf82b4e555babbb77c4a1706556b27e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bert?= <63123542+m-bert@users.noreply.github.com> Date: Fri, 14 Jun 2024 10:18:15 +0200 Subject: [PATCH] Unify scaled coordinates between `web` and `native` side. (#2943) ## Description On `android` and `iOS` if you scale view and get relative coords, they will act like they were not scaled at all. Let's say that we have a square with size `100x100`. If you click in the middle, `x` and `y` fields in event will both be `50`. Now, if you scale this view by 2 (i.e. new size is `200x200`) and click in the middle, you'll still get values of 50. Web takes scale into account and on the above situation, second output would be `100`. To unify this behavior we now divide coords by scale factors. ## Test plan Add `console.log(e.x, e.y)` in any example with scale, for example in `transformations` --- src/web/tools/PointerEventManager.ts | 12 +++++++--- src/web/tools/TouchEventManager.ts | 8 ++++--- src/web/utils.ts | 34 ++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/web/tools/PointerEventManager.ts b/src/web/tools/PointerEventManager.ts index d8892fcfc4..b2f72130b9 100644 --- a/src/web/tools/PointerEventManager.ts +++ b/src/web/tools/PointerEventManager.ts @@ -1,7 +1,11 @@ import EventManager from './EventManager'; import { MouseButton } from '../../handlers/gestureHandlerCommon'; import { AdaptedEvent, EventTypes, Point } from '../interfaces'; -import { PointerTypeMapping, isPointerInBounds } from '../utils'; +import { + PointerTypeMapping, + calculateViewScale, + isPointerInBounds, +} from '../utils'; import { PointerType } from '../../PointerType'; const POINTER_CAPTURE_EXCLUDE_LIST = new Set(['SELECT', 'INPUT']); @@ -234,11 +238,13 @@ export default class PointerEventManager extends EventManager { } protected mapEvent(event: PointerEvent, eventType: EventTypes): AdaptedEvent { + const { scaleX, scaleY } = calculateViewScale(this.view); + return { x: event.clientX, y: event.clientY, - offsetX: event.offsetX, - offsetY: event.offsetY, + offsetX: event.offsetX / scaleX, + offsetY: event.offsetY / scaleY, pointerId: event.pointerId, eventType: eventType, pointerType: diff --git a/src/web/tools/TouchEventManager.ts b/src/web/tools/TouchEventManager.ts index 6af20c1f94..60b1718351 100644 --- a/src/web/tools/TouchEventManager.ts +++ b/src/web/tools/TouchEventManager.ts @@ -1,6 +1,6 @@ import { AdaptedEvent, EventTypes, TouchEventType } from '../interfaces'; import EventManager from './EventManager'; -import { isPointerInBounds } from '../utils'; +import { calculateViewScale, isPointerInBounds } from '../utils'; import { PointerType } from '../../PointerType'; export default class TouchEventManager extends EventManager { @@ -156,11 +156,13 @@ export default class TouchEventManager extends EventManager { const clientX = event.changedTouches[index].clientX; const clientY = event.changedTouches[index].clientY; + const { scaleX, scaleY } = calculateViewScale(this.view); + return { x: clientX, y: clientY, - offsetX: clientX - rect.left, - offsetY: clientY - rect.top, + offsetX: (clientX - rect.left) / scaleX, + offsetY: (clientY - rect.top) / scaleY, pointerId: event.changedTouches[index].identifier, eventType: eventType, pointerType: PointerType.TOUCH, diff --git a/src/web/utils.ts b/src/web/utils.ts index 15fb6cf834..4b023eadf0 100644 --- a/src/web/utils.ts +++ b/src/web/utils.ts @@ -18,3 +18,37 @@ export const degToRad = (degrees: number) => (degrees * Math.PI) / 180; export const coneToDeviation = (degrees: number) => Math.cos(degToRad(degrees / 2)); + +export function calculateViewScale(view: HTMLElement) { + const styles = getComputedStyle(view); + + const resultScales = { + scaleX: 1, + scaleY: 1, + }; + + const scales = styles.scale.split(' '); + + if (scales[0] !== 'none') { + resultScales.scaleX = parseFloat(scales[0]); + } + + if (scales[1]) { + resultScales.scaleY = parseFloat(scales[1]); + } + + const matrixElements = new RegExp(/matrix\((.+)\)/).exec( + styles.transform + )?.[1]; + + if (!matrixElements) { + return resultScales; + } + + const matrixElementsArray = matrixElements.split(', '); + + resultScales.scaleX *= parseFloat(matrixElementsArray[0]); + resultScales.scaleY *= parseFloat(matrixElementsArray[3]); + + return resultScales; +}