From 83fa3d56ff8d56f9947202375ed972836601edb6 Mon Sep 17 00:00:00 2001 From: Nikita Minaev Date: Thu, 5 Dec 2024 14:59:45 +0300 Subject: [PATCH 1/3] fix(std): pan and pinch events were calculate relative to only one finger --- .../block-std/src/event/control/pointer.ts | 110 ++++++++++-------- packages/framework/global/src/utils/math.ts | 9 ++ tests/edgeless/basic.spec.ts | 8 +- 3 files changed, 72 insertions(+), 55 deletions(-) diff --git a/packages/framework/block-std/src/event/control/pointer.ts b/packages/framework/block-std/src/event/control/pointer.ts index fb5488fe7a27..98b32ba7c7a9 100644 --- a/packages/framework/block-std/src/event/control/pointer.ts +++ b/packages/framework/block-std/src/event/control/pointer.ts @@ -1,4 +1,5 @@ import { IS_IPAD } from '@blocksuite/global/env'; +import { dotProduct, magnitude } from '@blocksuite/global/utils'; import type { UIEventDispatcher } from '../dispatcher.js'; @@ -9,7 +10,7 @@ import { PointerEventState, } from '../state/index.js'; import { EventScopeSourceType, EventSourceState } from '../state/source.js'; -import { center, isFarEnough } from '../utils.js'; +import { isFarEnough } from '../utils.js'; type PointerId = typeof PointerEvent.prototype.pointerId; @@ -400,58 +401,41 @@ abstract class DualDragControllerBase extends PointerControllerBase { return; } - const { pointerId } = event; - - const start1 = - this._startPointerStates.primary.raw.pointerId === pointerId - ? this._startPointerStates.primary - : this._startPointerStates.secondary; - - const last1 = - (this._lastPointerStates.primary?.raw.pointerId === pointerId - ? this._lastPointerStates.primary - : this._lastPointerStates.secondary) ?? start1; - - if (!isFarEnough(last1.raw, event)) return; - - const state1 = new PointerEventState({ - event, - rect: this._rect, - startX: start1.x, - startY: start1.y, - last: last1, - }); - - const start2 = - this._startPointerStates.primary.raw.pointerId !== pointerId - ? this._startPointerStates.primary - : this._startPointerStates.secondary; + const { isPrimary } = event; + const startPrimaryState = this._startPointerStates.primary; + let lastPrimaryState = this._lastPointerStates.primary; - const last2 = - (this._lastPointerStates.primary?.raw.pointerId !== pointerId - ? this._lastPointerStates.primary - : this._lastPointerStates.secondary) ?? start2; + const startSecondaryState = this._startPointerStates.secondary; + let lastSecondaryState = this._lastPointerStates.secondary; - const state2 = new PointerEventState({ - event: last2.raw, - rect: this._rect, - startX: start2.x, - startY: start2.y, - last: last2, - }); - - if (!isFarEnough(state1.delta, state2.delta)) return; + if (isPrimary) { + lastPrimaryState = new PointerEventState({ + event, + rect: this._rect, + startX: startPrimaryState.x, + startY: startPrimaryState.y, + last: lastPrimaryState, + }); + } else { + lastSecondaryState = new PointerEventState({ + event, + rect: this._rect, + startX: startSecondaryState.x, + startY: startSecondaryState.y, + last: lastSecondaryState, + }); + } const multiPointerState = new MultiPointerEventState(event, [ - state1, - state2, + lastPrimaryState ?? startPrimaryState, + lastSecondaryState ?? startSecondaryState, ]); this._handleMove(event, multiPointerState); this._lastPointerStates = { - primary: state1.raw.isPrimary ? state1 : state2, - secondary: state1.raw.isPrimary ? state2 : state1, + primary: lastPrimaryState, + secondary: lastSecondaryState, }; }; @@ -502,8 +486,27 @@ class PinchController extends DualDragControllerBase { override _handleMove(event: PointerEvent, state: MultiPointerEventState) { if (event.pointerType !== 'touch') return; + const deltaFirstPointerVec = state.pointers[0].delta; + const deltaSecondPointerVec = state.pointers[1].delta; + + const deltaFirstPointerValue = magnitude(deltaFirstPointerVec); + const deltaSecondPointerValue = magnitude(deltaSecondPointerVec); + + const deltaDotProduct = dotProduct( + deltaFirstPointerVec, + deltaSecondPointerVec + ); + + const deltaValueThreshold = 0.1; + // the changes of distance between two pointers is not far enough - if (!isFarEnough(state.pointers[0].delta, state.pointers[1].delta)) return; + if ( + !isFarEnough(deltaFirstPointerVec, deltaSecondPointerVec) || + deltaDotProduct > 0 || + deltaFirstPointerValue < deltaValueThreshold || + deltaSecondPointerValue < deltaValueThreshold + ) + return; this._dispatcher.run('pinch', createContext(event, state)); } @@ -513,15 +516,20 @@ class PanController extends DualDragControllerBase { override _handleMove(event: PointerEvent, state: MultiPointerEventState) { if (event.pointerType !== 'touch') return; - // current center, last center = center move vector - // 0.5 * (a + da + b + db) - 0.5 * (a + b) = 0.5 * (da + db) - const centerMoveVec = center( - state.pointers[0].delta, - state.pointers[1].delta + const deltaFirstPointerVec = state.pointers[0].delta; + const deltaSecondPointerVec = state.pointers[1].delta; + + const deltaDotProduct = dotProduct( + deltaFirstPointerVec, + deltaSecondPointerVec ); // the center move distance is not far enough - if (!isFarEnough({ x: 0, y: 0 }, centerMoveVec)) return; + if ( + !isFarEnough(deltaFirstPointerVec, deltaSecondPointerVec) && + deltaDotProduct < 0 + ) + return; this._dispatcher.run('pan', createContext(event, state)); } diff --git a/packages/framework/global/src/utils/math.ts b/packages/framework/global/src/utils/math.ts index 0bb93c72f24c..c17ad9702b12 100644 --- a/packages/framework/global/src/utils/math.ts +++ b/packages/framework/global/src/utils/math.ts @@ -1,3 +1,4 @@ +import type { IPoint } from '../utils.js'; import type { Bound, IBound } from './model/bound.js'; import { PointLocation } from './model/point-location.js'; @@ -536,3 +537,11 @@ export function getCenterAreaBounds(bounds: IBound, ratio: number) { rotate, }; } + +export function dotProduct(vectorA: IPoint, vectorB: IPoint) { + return vectorA.x * vectorB.x + vectorA.y * vectorB.y; +} + +export function magnitude(vector: IPoint) { + return Math.sqrt(vector.x * vector.x + vector.y * vector.y); +} diff --git a/tests/edgeless/basic.spec.ts b/tests/edgeless/basic.spec.ts index 802ec72d4445..392a6cb059a9 100644 --- a/tests/edgeless/basic.spec.ts +++ b/tests/edgeless/basic.spec.ts @@ -139,8 +139,8 @@ test('zoom by pinch', async ({ page }) => { { x: CENTER_X + 100, y: CENTER_Y }, ]; const to = [ - { x: CENTER_X - 50, y: CENTER_Y }, - { x: CENTER_X + 50, y: CENTER_Y }, + { x: CENTER_X - 50, y: CENTER_Y - 35 }, + { x: CENTER_X + 50, y: CENTER_Y + 35 }, ]; await multiTouchDown(page, from); await multiTouchMove(page, from, to); @@ -166,8 +166,8 @@ test('zoom by pinch when edgeless is readonly', async ({ page }) => { { x: CENTER_X + 100, y: CENTER_Y }, ]; const to = [ - { x: CENTER_X - 50, y: CENTER_Y }, - { x: CENTER_X + 50, y: CENTER_Y }, + { x: CENTER_X - 50, y: CENTER_Y - 35 }, + { x: CENTER_X + 50, y: CENTER_Y + 35 }, ]; await multiTouchDown(page, from); await multiTouchMove(page, from, to); From 2345fa10f2932212b933f787d3a2db752025c77f Mon Sep 17 00:00:00 2001 From: Nikita Minaev Date: Thu, 5 Dec 2024 15:24:04 +0300 Subject: [PATCH 2/3] fix(std): add two deltas constants --- .../blocks/src/root-block/edgeless/edgeless-root-block.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blocks/src/root-block/edgeless/edgeless-root-block.ts b/packages/blocks/src/root-block/edgeless/edgeless-root-block.ts index ebc958215b2f..68e25d8589f9 100644 --- a/packages/blocks/src/root-block/edgeless/edgeless-root-block.ts +++ b/packages/blocks/src/root-block/edgeless/edgeless-root-block.ts @@ -224,9 +224,9 @@ export class EdgelessRootBlockComponent extends BlockComponent< const [p1, p2] = multiPointersState.pointers; const dx = - (0.5 * (p1.delta.x + p2.delta.x)) / viewport.zoom / viewport.scale; + (0.25 * (p1.delta.x + p2.delta.x)) / viewport.zoom / viewport.scale; const dy = - (0.5 * (p1.delta.y + p2.delta.y)) / viewport.zoom / viewport.scale; + (0.25 * (p1.delta.y + p2.delta.y)) / viewport.zoom / viewport.scale; // direction is opposite viewport.applyDeltaCenter(-dx, -dy); From 22e87c5316268094dbe4996fdf2dd01f2fbab4d0 Mon Sep 17 00:00:00 2001 From: Nikita Minaev Date: Thu, 5 Dec 2024 15:54:44 +0300 Subject: [PATCH 3/3] refactor(std): replace dot,mag from own to math --- .../block-std/src/event/control/pointer.ts | 29 ++++++++++--------- packages/framework/global/src/utils/math.ts | 9 ------ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/packages/framework/block-std/src/event/control/pointer.ts b/packages/framework/block-std/src/event/control/pointer.ts index 98b32ba7c7a9..e8f1b213f5e5 100644 --- a/packages/framework/block-std/src/event/control/pointer.ts +++ b/packages/framework/block-std/src/event/control/pointer.ts @@ -1,5 +1,5 @@ import { IS_IPAD } from '@blocksuite/global/env'; -import { dotProduct, magnitude } from '@blocksuite/global/utils'; +import { Vec } from '@blocksuite/global/utils'; import type { UIEventDispatcher } from '../dispatcher.js'; @@ -486,13 +486,16 @@ class PinchController extends DualDragControllerBase { override _handleMove(event: PointerEvent, state: MultiPointerEventState) { if (event.pointerType !== 'touch') return; - const deltaFirstPointerVec = state.pointers[0].delta; - const deltaSecondPointerVec = state.pointers[1].delta; + const deltaFirstPointer = state.pointers[0].delta; + const deltaSecondPointer = state.pointers[1].delta; - const deltaFirstPointerValue = magnitude(deltaFirstPointerVec); - const deltaSecondPointerValue = magnitude(deltaSecondPointerVec); + const deltaFirstPointerVec = Vec.toVec(deltaFirstPointer); + const deltaSecondPointerVec = Vec.toVec(deltaSecondPointer); - const deltaDotProduct = dotProduct( + const deltaFirstPointerValue = Vec.len(deltaFirstPointerVec); + const deltaSecondPointerValue = Vec.len(deltaSecondPointerVec); + + const deltaDotProduct = Vec.dpr( deltaFirstPointerVec, deltaSecondPointerVec ); @@ -501,7 +504,7 @@ class PinchController extends DualDragControllerBase { // the changes of distance between two pointers is not far enough if ( - !isFarEnough(deltaFirstPointerVec, deltaSecondPointerVec) || + !isFarEnough(deltaFirstPointer, deltaSecondPointer) || deltaDotProduct > 0 || deltaFirstPointerValue < deltaValueThreshold || deltaSecondPointerValue < deltaValueThreshold @@ -516,17 +519,17 @@ class PanController extends DualDragControllerBase { override _handleMove(event: PointerEvent, state: MultiPointerEventState) { if (event.pointerType !== 'touch') return; - const deltaFirstPointerVec = state.pointers[0].delta; - const deltaSecondPointerVec = state.pointers[1].delta; + const deltaFirstPointer = state.pointers[0].delta; + const deltaSecondPointer = state.pointers[1].delta; - const deltaDotProduct = dotProduct( - deltaFirstPointerVec, - deltaSecondPointerVec + const deltaDotProduct = Vec.dpr( + Vec.toVec(deltaFirstPointer), + Vec.toVec(deltaSecondPointer) ); // the center move distance is not far enough if ( - !isFarEnough(deltaFirstPointerVec, deltaSecondPointerVec) && + !isFarEnough(deltaFirstPointer, deltaSecondPointer) && deltaDotProduct < 0 ) return; diff --git a/packages/framework/global/src/utils/math.ts b/packages/framework/global/src/utils/math.ts index c17ad9702b12..0bb93c72f24c 100644 --- a/packages/framework/global/src/utils/math.ts +++ b/packages/framework/global/src/utils/math.ts @@ -1,4 +1,3 @@ -import type { IPoint } from '../utils.js'; import type { Bound, IBound } from './model/bound.js'; import { PointLocation } from './model/point-location.js'; @@ -537,11 +536,3 @@ export function getCenterAreaBounds(bounds: IBound, ratio: number) { rotate, }; } - -export function dotProduct(vectorA: IPoint, vectorB: IPoint) { - return vectorA.x * vectorB.x + vectorA.y * vectorB.y; -} - -export function magnitude(vector: IPoint) { - return Math.sqrt(vector.x * vector.x + vector.y * vector.y); -}