From a2978bd1f507e9417b995bbb0b7ff756dbe5d2c4 Mon Sep 17 00:00:00 2001 From: Hufe Date: Fri, 21 Jun 2024 21:19:41 +0800 Subject: [PATCH] feat: add mouse event listener #603 --- docs/en/guide/command-get.md | 26 +++++++++ docs/en/guide/eventbus.md | 30 +++++++++++ docs/guide/command-get.md | 26 +++++++++ docs/guide/eventbus.md | 30 +++++++++++ src/editor/core/command/Command.ts | 2 + src/editor/core/command/CommandAdapt.ts | 65 ++++++++++++++++++++++- src/editor/core/cursor/CursorAgent.ts | 7 +-- src/editor/core/draw/Draw.ts | 2 + src/editor/core/observer/MouseObserver.ts | 38 +++++++++++++ src/editor/core/position/Position.ts | 2 + src/editor/interface/Event.ts | 9 ++++ src/editor/interface/EventBus.ts | 4 ++ src/editor/interface/Listener.ts | 2 + src/editor/utils/index.ts | 14 +++-- src/utils/index.ts | 7 ++- 15 files changed, 254 insertions(+), 10 deletions(-) create mode 100644 src/editor/core/observer/MouseObserver.ts diff --git a/docs/en/guide/command-get.md b/docs/en/guide/command-get.md index 3fb6387..8421505 100644 --- a/docs/en/guide/command-get.md +++ b/docs/en/guide/command-get.md @@ -241,3 +241,29 @@ const { zone: EditorZone }[] = await instance.command.getTitleValue(payload: IGetTitleValueOption) ``` + +## getPositionContextByEvent + +Feature: Get position context by mouse event + +Usage: + +```javascript +const { + pageNo: number + element: IElement | null + rangeRect: RangeRect | null +}[] = await instance.command.getPositionContextByEvent(evt: MouseEvent) +``` + +demo: + +```javascript +instance.eventBus.on( + 'mousemove', + debounce(evt => { + const positionContext = instance.command.getPositionContextByEvent(evt) + console.log(positionContext) + }, 200) +)`` +``` diff --git a/docs/en/guide/eventbus.md b/docs/en/guide/eventbus.md index f27ac5d..90e4cc3 100644 --- a/docs/en/guide/eventbus.md +++ b/docs/en/guide/eventbus.md @@ -119,3 +119,33 @@ Usage: ```javascript instance.eventBus.on('zoneChange', (payload: EditorZone) => void) ``` + +## mousemove + +Feature: Editor mousemove event + +Usage: + +```javascript +instance.eventBus.on('mousemove', (evt: MouseEvent) => void) +``` + +## mouseenter + +Feature: Editor mouseenter event + +Usage: + +```javascript +instance.eventBus.on('mouseenter', (evt: MouseEvent) => void) +``` + +## mouseleave + +Feature: Editor mouseleave event + +Usage: + +```javascript +instance.eventBus.on('mouseleave', (evt: MouseEvent) => void) +``` diff --git a/docs/guide/command-get.md b/docs/guide/command-get.md index c7541eb..e540414 100644 --- a/docs/guide/command-get.md +++ b/docs/guide/command-get.md @@ -242,3 +242,29 @@ const { zone: EditorZone }[] = await instance.command.getTitleValue(payload: IGetTitleValueOption) ``` + +## getPositionContextByEvent + +功能:获取位置上下文信息通过鼠标事件 + +用法: + +```javascript +const { + pageNo: number + element: IElement | null + rangeRect: RangeRect | null +}[] = await instance.command.getPositionContextByEvent(evt: MouseEvent) +``` + +示例: + +```javascript +instance.eventBus.on( + 'mousemove', + debounce(evt => { + const positionContext = instance.command.getPositionContextByEvent(evt) + console.log(positionContext) + }, 200) +)`` +``` diff --git a/docs/guide/eventbus.md b/docs/guide/eventbus.md index 9fa2a2f..4ff8426 100644 --- a/docs/guide/eventbus.md +++ b/docs/guide/eventbus.md @@ -119,3 +119,33 @@ instance.eventBus.on('saved', (payload: IEditorResult) => void) ```javascript instance.eventBus.on('zoneChange', (payload: EditorZone) => void) ``` + +## mousemove + +功能:编辑器 mousemove 事件监听 + +用法: + +```javascript +instance.eventBus.on('mousemove', (evt: MouseEvent) => void) +``` + +## mouseenter + +功能:编辑器 mouseenter 事件监听 + +用法: + +```javascript +instance.eventBus.on('mouseenter', (evt: MouseEvent) => void) +``` + +## mouseleave + +功能:编辑器 mouseleave 事件监听 + +用法: + +```javascript +instance.eventBus.on('mouseleave', (evt: MouseEvent) => void) +``` diff --git a/src/editor/core/command/Command.ts b/src/editor/core/command/Command.ts index 6e843a7..43e82e9 100644 --- a/src/editor/core/command/Command.ts +++ b/src/editor/core/command/Command.ts @@ -115,6 +115,7 @@ export class Command { public getControlList: CommandAdapt['getControlList'] public getContainer: CommandAdapt['getContainer'] public getTitleValue: CommandAdapt['getTitleValue'] + public getPositionContextByEvent: CommandAdapt['getPositionContextByEvent'] constructor(adapt: CommandAdapt) { // 全局命令 @@ -232,6 +233,7 @@ export class Command { this.getGroupIds = adapt.getGroupIds.bind(adapt) this.getContainer = adapt.getContainer.bind(adapt) this.getTitleValue = adapt.getTitleValue.bind(adapt) + this.getPositionContextByEvent = adapt.getPositionContextByEvent.bind(adapt) // 控件 this.executeSetControlValue = adapt.setControlValue.bind(adapt) this.executeSetControlExtension = adapt.setControlExtension.bind(adapt) diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts index 87b72cc..dae3260 100644 --- a/src/editor/core/command/CommandAdapt.ts +++ b/src/editor/core/command/CommandAdapt.ts @@ -55,10 +55,11 @@ import { } from '../../interface/Editor' import { IElement, + IElementPosition, IElementStyle, IUpdateElementByIdOption } from '../../interface/Element' -import { IPasteOption } from '../../interface/Event' +import { IPasteOption, IPositionContextByEvent } from '../../interface/Event' import { IMargin } from '../../interface/Margin' import { ILocationPosition } from '../../interface/Position' import { IRange, RangeContext, RangeRect } from '../../interface/Range' @@ -102,6 +103,7 @@ import { I18n } from '../i18n/I18n' import { Position } from '../position/Position' import { RangeManager } from '../range/RangeManager' import { WorkerManager } from '../worker/WorkerManager' +import { Zone } from '../zone/Zone' export class CommandAdapt { private draw: Draw @@ -115,6 +117,7 @@ export class CommandAdapt { private workerManager: WorkerManager private searchManager: Search private i18n: I18n + private zone: Zone constructor(draw: Draw) { this.draw = draw @@ -128,6 +131,7 @@ export class CommandAdapt { this.workerManager = draw.getWorkerManager() this.searchManager = draw.getSearch() this.i18n = draw.getI18n() + this.zone = draw.getZone() } public mode(payload: EditorMode) { @@ -2637,6 +2641,65 @@ export class CommandAdapt { return result } + public getPositionContextByEvent( + evt: MouseEvent + ): IPositionContextByEvent | null { + const pageIndex = (evt.target)?.dataset.index + if (!pageIndex) return null + const pageNo = Number(pageIndex) + const positionContext = this.position.getPositionByXY({ + x: evt.offsetX, + y: evt.offsetY, + pageNo + }) + const { + isDirectHit, + isTable, + index, + trIndex, + tdIndex, + tdValueIndex, + zone + } = positionContext + // 非直接命中或选区不一致时返回空值 + if (!isDirectHit || (zone && zone !== this.zone.getZone())) return null + // 命中元素信息 + let element: IElement | null = null + const elementList = this.draw.getOriginalElementList() + let position: IElementPosition | null = null + const positionList = this.position.getOriginalPositionList() + if (isTable) { + const td = elementList[index!].trList?.[trIndex!].tdList[tdIndex!] + element = td?.value[tdValueIndex!] || null + position = td?.positionList?.[tdValueIndex!] || null + } else { + element = elementList[index] || null + position = positionList[index] || null + } + // 元素包围信息 + let rangeRect: RangeRect | null = null + if (position) { + const { + pageNo, + coordinate: { leftTop, rightTop }, + lineHeight + } = position + const height = this.draw.getOriginalHeight() + const pageGap = this.draw.getOriginalPageGap() + rangeRect = { + x: leftTop[0], + y: leftTop[1] + pageNo * (height + pageGap), + width: rightTop[0] - leftTop[0], + height: lineHeight + } + } + return { + pageNo, + element, + rangeRect + } + } + public insertTitle(payload: IElement) { const isReadonly = this.draw.isReadonly() if (isReadonly) return diff --git a/src/editor/core/cursor/CursorAgent.ts b/src/editor/core/cursor/CursorAgent.ts index b351773..cb11b7a 100644 --- a/src/editor/core/cursor/CursorAgent.ts +++ b/src/editor/core/cursor/CursorAgent.ts @@ -43,9 +43,10 @@ export class CursorAgent { this.canvasEvent.keydown(evt) } - private _input(evt: InputEvent) { - if (!evt.data) return - this.canvasEvent.input(evt.data) + private _input(evt: Event) { + const data = (evt).data + if (!data) return + this.canvasEvent.input(data) } private _paste(evt: ClipboardEvent) { diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index a5f83eb..8be0b78 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -99,6 +99,7 @@ import { Override } from '../override/Override' import { ImageDisplay } from '../../dataset/enum/Common' import { PUNCTUATION_REG } from '../../dataset/constant/Regular' import { LineBreakParticle } from './particle/LineBreakParticle' +import { MouseObserver } from '../observer/MouseObserver' export class Draw { private container: HTMLDivElement @@ -231,6 +232,7 @@ export class Draw { this.scrollObserver = new ScrollObserver(this) this.selectionObserver = new SelectionObserver(this) this.imageObserver = new ImageObserver() + new MouseObserver(this) this.canvasEvent = new CanvasEvent(this) this.cursor = new Cursor(this, this.canvasEvent) diff --git a/src/editor/core/observer/MouseObserver.ts b/src/editor/core/observer/MouseObserver.ts new file mode 100644 index 0000000..cf8c8c9 --- /dev/null +++ b/src/editor/core/observer/MouseObserver.ts @@ -0,0 +1,38 @@ +import { EventBusMap } from '../../interface/EventBus' +import { Draw } from '../draw/Draw' +import { EventBus } from '../event/eventbus/EventBus' + +export class MouseObserver { + private draw: Draw + private eventBus: EventBus + private pageContainer: HTMLDivElement + constructor(draw: Draw) { + this.draw = draw + this.eventBus = this.draw.getEventBus() + this.pageContainer = this.draw.getPageContainer() + this.pageContainer.addEventListener('mousemove', this._mousemove.bind(this)) + this.pageContainer.addEventListener( + 'mouseenter', + this._mouseenter.bind(this) + ) + this.pageContainer.addEventListener( + 'mouseleave', + this._mouseleave.bind(this) + ) + } + + private _mousemove(evt: MouseEvent) { + if (!this.eventBus.isSubscribe('mousemove')) return + this.eventBus.emit('mousemove', evt) + } + + private _mouseenter(evt: MouseEvent) { + if (!this.eventBus.isSubscribe('mouseenter')) return + this.eventBus.emit('mouseenter', evt) + } + + private _mouseleave(evt: MouseEvent) { + if (!this.eventBus.isSubscribe('mouseleave')) return + this.eventBus.emit('mouseleave', evt) + } +} diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts index 3156929..8a4a255 100644 --- a/src/editor/core/position/Position.ts +++ b/src/editor/core/position/Position.ts @@ -383,6 +383,7 @@ export class Position { x, y, td, + pageNo: curPageNo, tablePosition: positionList[j], isTable: true, elementList: td.value, @@ -460,6 +461,7 @@ export class Position { } } return { + isDirectHit: true, hitLineStartIndex, index: curPositionIndex, isControl: !!element.controlId diff --git a/src/editor/interface/Event.ts b/src/editor/interface/Event.ts index 88a3eee..dc537bb 100644 --- a/src/editor/interface/Event.ts +++ b/src/editor/interface/Event.ts @@ -1,3 +1,12 @@ +import { IElement } from './Element' +import { RangeRect } from './Range' + export interface IPasteOption { isPlainText: boolean } + +export interface IPositionContextByEvent { + pageNo: number + element: IElement | null + rangeRect: RangeRect | null +} diff --git a/src/editor/interface/EventBus.ts b/src/editor/interface/EventBus.ts index 0c047cf..ef60b8a 100644 --- a/src/editor/interface/EventBus.ts +++ b/src/editor/interface/EventBus.ts @@ -2,6 +2,7 @@ import { IContentChange, IControlChange, IIntersectionPageNoChange, + IMouseEventChange, IPageModeChange, IPageScaleChange, IPageSizeChange, @@ -22,4 +23,7 @@ export interface EventBusMap { controlChange: IControlChange pageModeChange: IPageModeChange zoneChange: IZoneChange + mousemove: IMouseEventChange + mouseleave: IMouseEventChange + mouseenter: IMouseEventChange } diff --git a/src/editor/interface/Listener.ts b/src/editor/interface/Listener.ts index feb4649..51ee80c 100644 --- a/src/editor/interface/Listener.ts +++ b/src/editor/interface/Listener.ts @@ -54,3 +54,5 @@ export type IControlChange = (payload: IControl | null) => void export type IPageModeChange = (payload: PageMode) => void export type IZoneChange = (payload: EditorZone) => void + +export type IMouseEventChange = (evt: MouseEvent) => void diff --git a/src/editor/utils/index.ts b/src/editor/utils/index.ts index 4e918a0..60c8a27 100644 --- a/src/editor/utils/index.ts +++ b/src/editor/utils/index.ts @@ -1,8 +1,11 @@ import { UNICODE_SYMBOL_REG } from '../dataset/constant/Regular' -export function debounce(func: Function, delay: number) { +export function debounce( + func: (...arg: T) => unknown, + delay: number +) { let timer: number - return function (this: any, ...args: any[]) { + return function (this: unknown, ...args: T) { if (timer) { window.clearTimeout(timer) } @@ -12,10 +15,13 @@ export function debounce(func: Function, delay: number) { } } -export function throttle(func: Function, delay: number) { +export function throttle( + func: (...arg: T) => unknown, + delay: number +) { let lastExecTime = 0 let timer: number - return function (this: any, ...args: any[]) { + return function (this: unknown, ...args: T) { const currentTime = Date.now() if (currentTime - lastExecTime >= delay) { window.clearTimeout(timer) diff --git a/src/utils/index.ts b/src/utils/index.ts index 7bfc5f7..5bf47f5 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,6 +1,9 @@ -export function debounce(func: Function, delay: number) { +export function debounce( + func: (...arg: T) => unknown, + delay: number +) { let timer: number - return function (this: any, ...args: any[]) { + return function (this: unknown, ...args: T) { if (timer) { window.clearTimeout(timer) }