From 04bc508a5b265a7b7d48c121d4ec778801742e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=B1=AA=E7=8F=A3?= <519367854@qq.com> Date: Tue, 12 Mar 2024 22:56:49 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E5=AE=8C=E5=96=84=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/core/drivers/drag-drop-driver.ts | 116 ++++++++++++++++++ .../core/events/cursor/AbstractCursorEvent.ts | 57 +++++++++ src/app/core/events/cursor/DragMoveEvent.ts | 6 + src/app/core/events/cursor/DragStartEvent.ts | 6 + src/app/core/events/cursor/DragStopEvent.ts | 6 + src/app/core/events/cursor/MouseClickEvent.ts | 10 ++ src/app/core/events/cursor/MouseMoveEvent.ts | 6 + src/app/core/events/cursor/index.ts | 5 + 8 files changed, 212 insertions(+) create mode 100644 src/app/core/events/cursor/AbstractCursorEvent.ts create mode 100644 src/app/core/events/cursor/DragMoveEvent.ts create mode 100644 src/app/core/events/cursor/DragStartEvent.ts create mode 100644 src/app/core/events/cursor/DragStopEvent.ts create mode 100644 src/app/core/events/cursor/MouseClickEvent.ts create mode 100644 src/app/core/events/cursor/MouseMoveEvent.ts create mode 100644 src/app/core/events/cursor/index.ts diff --git a/src/app/core/drivers/drag-drop-driver.ts b/src/app/core/drivers/drag-drop-driver.ts index a8ce06b..bebaf1d 100644 --- a/src/app/core/drivers/drag-drop-driver.ts +++ b/src/app/core/drivers/drag-drop-driver.ts @@ -1,8 +1,124 @@ import { EventDriver } from '../../shared/event'; import { Engine } from '../models'; +import { DragMoveEvent, DragStartEvent, DragStopEvent } from '../events/cursor'; + +const GlobalState = { + dragging: false, + onMouseDownAt: 0, + startEvent: null, + moveEvent: null +}; export class DragDropDriver extends EventDriver { mouseDownTimer = null; startEvent: MouseEvent; + + onMouseDown = (e: MouseEvent) => { + if (e.button !== 0 || e.ctrlKey || e.metaKey) { + return; + } + if (e.target['isContentEditable'] || e.target['contentEditable'] === 'true') { + return true; + } + if (e.target?.['closest']?.('.monaco-editor')) return; + GlobalState.startEvent = e; + GlobalState.dragging = false; + GlobalState.onMouseDownAt = Date.now(); + this.batchAddEventListener('mouseup', this.onMouseUp); + this.batchAddEventListener('dragend', this.onMouseUp); + this.batchAddEventListener('dragstart', this.onStartDrag); + this.batchAddEventListener('mousemove', this.onDistanceChange); + return; + }; + + onMouseUp = (e: MouseEvent) => { + if (GlobalState.dragging) { + this.dispatch( + new DragStopEvent({ + clientX: e.clientX, + clientY: e.clientY, + pageX: e.pageX, + pageY: e.pageY, + target: e.target, + view: e.view + }) + ); + } + this.batchRemoveEventListener('contextmenu', this.onContextMenuWhileDragging, true); + this.batchRemoveEventListener('mouseup', this.onMouseUp); + this.batchRemoveEventListener('mousedown', this.onMouseDown); + this.batchRemoveEventListener('dragover', this.onMouseMove); + this.batchRemoveEventListener('mousemove', this.onMouseMove); + this.batchRemoveEventListener('mousemove', this.onDistanceChange); + GlobalState.dragging = false; + }; + + onMouseMove = (e: MouseEvent | DragEvent) => { + if (e.clientX === GlobalState.moveEvent?.clientX && e.clientY === GlobalState.moveEvent?.clientY) return; + this.dispatch( + new DragMoveEvent({ + clientX: e.clientX, + clientY: e.clientY, + pageX: e.pageX, + pageY: e.pageY, + target: e.target, + view: e.view + }) + ); + GlobalState.moveEvent = e; + }; + + onContextMenuWhileDragging = (e: MouseEvent) => { + e.preventDefault(); + }; + + onStartDrag = (e: MouseEvent | DragEvent) => { + if (GlobalState.dragging) return; + GlobalState.startEvent = GlobalState.startEvent || e; + this.batchAddEventListener('dragover', this.onMouseMove); + this.batchAddEventListener('mousemove', this.onMouseMove); + this.batchAddEventListener('contextmenu', this.onContextMenuWhileDragging, true); + this.dispatch( + new DragStartEvent({ + clientX: GlobalState.startEvent.clientX, + clientY: GlobalState.startEvent.clientY, + pageX: GlobalState.startEvent.pageX, + pageY: GlobalState.startEvent.pageY, + target: GlobalState.startEvent.target, + view: GlobalState.startEvent.view + }) + ); + GlobalState.dragging = true; + }; + + onDistanceChange = (e: MouseEvent) => { + const distance = Math.sqrt( + Math.pow(e.pageX - GlobalState.startEvent.pageX, 2) + Math.pow(e.pageY - GlobalState.startEvent.pageY, 2) + ); + const timeDelta = Date.now() - GlobalState.onMouseDownAt; + if (timeDelta > 10 && e !== GlobalState.startEvent && distance > 4) { + this.batchRemoveEventListener('mousemove', this.onDistanceChange); + this.onStartDrag(e); + } + }; + + override attach() { + this.batchAddEventListener('mousedown', this.onMouseDown, true); + } + + override detach() { + GlobalState.dragging = false; + GlobalState.moveEvent = null; + GlobalState.onMouseDownAt = null; + GlobalState.startEvent = null; + this.batchRemoveEventListener('mousedown', this.onMouseDown, true); + this.batchRemoveEventListener('dragstart', this.onStartDrag); + this.batchRemoveEventListener('dragend', this.onMouseUp); + this.batchRemoveEventListener('dragover', this.onMouseMove); + this.batchRemoveEventListener('mouseup', this.onMouseUp); + this.batchRemoveEventListener('mousemove', this.onMouseMove); + this.batchRemoveEventListener('mousemove', this.onDistanceChange); + this.batchRemoveEventListener('contextmenu', this.onContextMenuWhileDragging, true); + } } diff --git a/src/app/core/events/cursor/AbstractCursorEvent.ts b/src/app/core/events/cursor/AbstractCursorEvent.ts new file mode 100644 index 0000000..165287f --- /dev/null +++ b/src/app/core/events/cursor/AbstractCursorEvent.ts @@ -0,0 +1,57 @@ +import { IEngineContext } from '../../types'; +import { globalThisPolyfill } from '../../../shared/globalThisPolyfill'; + +export interface ICursorEventOriginData { + clientX: number; + clientY: number; + pageX: number; + pageY: number; + target: EventTarget; + view: Window; +} + +export interface ICursorEventData extends ICursorEventOriginData { + topClientX?: number; + topClientY?: number; + topPageX?: number; + topPageY?: number; +} + +export class AbstractCursorEvent { + data: ICursorEventData; + + context: IEngineContext; + + constructor(data: ICursorEventOriginData) { + this.data = data || { + clientX: 0, + clientY: 0, + pageX: 0, + pageY: 0, + target: null, + view: globalThisPolyfill + }; + this.transformCoordinates(); + } + + transformCoordinates() { + const { frameElement } = this.data?.view || {}; + if (frameElement && this.data.view !== globalThisPolyfill) { + const frameRect = frameElement.getBoundingClientRect(); + const scale = frameRect.width / frameElement['offsetWidth']; + this.data.topClientX = this.data.clientX * scale + frameRect.x; + this.data.topClientY = this.data.clientY * scale + frameRect.y; + this.data.topPageX = this.data.pageX + frameRect.x - this.data.view.scrollX; + this.data.topPageY = this.data.pageY + frameRect.y - this.data.view.scrollY; + const topElement = document.elementFromPoint(this.data.topPageX, this.data.topClientY); + if (topElement !== frameElement) { + this.data.target = topElement; + } + } else { + this.data.topClientX = this.data.clientX; + this.data.topClientY = this.data.clientY; + this.data.topPageX = this.data.pageX; + this.data.topPageY = this.data.pageY; + } + } +} diff --git a/src/app/core/events/cursor/DragMoveEvent.ts b/src/app/core/events/cursor/DragMoveEvent.ts new file mode 100644 index 0000000..4c142c0 --- /dev/null +++ b/src/app/core/events/cursor/DragMoveEvent.ts @@ -0,0 +1,6 @@ +import { AbstractCursorEvent } from './AbstractCursorEvent'; +import { ICustomEvent } from '../../../shared/event'; + +export class DragMoveEvent extends AbstractCursorEvent implements ICustomEvent { + type = 'drag:move'; +} diff --git a/src/app/core/events/cursor/DragStartEvent.ts b/src/app/core/events/cursor/DragStartEvent.ts new file mode 100644 index 0000000..3889057 --- /dev/null +++ b/src/app/core/events/cursor/DragStartEvent.ts @@ -0,0 +1,6 @@ +import { AbstractCursorEvent } from './AbstractCursorEvent'; +import { ICustomEvent } from '../../../shared/event'; + +export class DragStartEvent extends AbstractCursorEvent implements ICustomEvent { + type = 'drag:start'; +} diff --git a/src/app/core/events/cursor/DragStopEvent.ts b/src/app/core/events/cursor/DragStopEvent.ts new file mode 100644 index 0000000..25012f8 --- /dev/null +++ b/src/app/core/events/cursor/DragStopEvent.ts @@ -0,0 +1,6 @@ +import { AbstractCursorEvent } from './AbstractCursorEvent'; +import { ICustomEvent } from '../../../shared/event'; + +export class DragStopEvent extends AbstractCursorEvent implements ICustomEvent { + type = 'drag:stop'; +} diff --git a/src/app/core/events/cursor/MouseClickEvent.ts b/src/app/core/events/cursor/MouseClickEvent.ts new file mode 100644 index 0000000..7773658 --- /dev/null +++ b/src/app/core/events/cursor/MouseClickEvent.ts @@ -0,0 +1,10 @@ +import { AbstractCursorEvent } from './AbstractCursorEvent'; +import { ICustomEvent } from '../../../shared/event'; + +export class MouseClickEvent extends AbstractCursorEvent implements ICustomEvent { + type = 'mouse:click'; +} + +export class MouseDoubleClickEvent extends AbstractCursorEvent implements ICustomEvent { + type = 'mouse:dblclick'; +} diff --git a/src/app/core/events/cursor/MouseMoveEvent.ts b/src/app/core/events/cursor/MouseMoveEvent.ts new file mode 100644 index 0000000..1f52978 --- /dev/null +++ b/src/app/core/events/cursor/MouseMoveEvent.ts @@ -0,0 +1,6 @@ +import { AbstractCursorEvent } from './AbstractCursorEvent'; +import { ICustomEvent } from '../../../shared/event'; + +export class MouseMoveEvent extends AbstractCursorEvent implements ICustomEvent { + type = 'mouse:move'; +} diff --git a/src/app/core/events/cursor/index.ts b/src/app/core/events/cursor/index.ts new file mode 100644 index 0000000..f0a94b7 --- /dev/null +++ b/src/app/core/events/cursor/index.ts @@ -0,0 +1,5 @@ +export * from './DragMoveEvent'; +export * from './DragStartEvent'; +export * from './DragStopEvent'; +export * from './MouseClickEvent'; +export * from './MouseMoveEvent';