From a2fb92d9880bc852cd630b8354a40f3378f96902 Mon Sep 17 00:00:00 2001 From: parisma Date: Mon, 25 Jul 2022 15:40:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(stage):=201)=20=E9=AB=98=E4=BA=AE=E8=BE=B9?= =?UTF-8?q?=E6=A1=86=E6=A0=B7=E5=BC=8F=E5=8A=A0=E7=B2=97=202)=20=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E7=BB=84=E4=BB=B6=E6=98=AF=E5=90=A6=E5=8F=AF=E9=80=89?= =?UTF-8?q?=E4=B8=AD=E9=80=BB=E8=BE=91,=E5=9B=BA=E5=AE=9A=E5=AE=9A?= =?UTF-8?q?=E4=BD=8D=E5=92=8C=E7=BB=9D=E5=AF=B9=E5=AE=9A=E4=BD=8D=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E6=B7=B7=E5=90=88=E5=A4=9A=E9=80=89=203)=20=E5=A4=9A?= =?UTF-8?q?=E9=80=89moveable=20options=E6=8E=A5=E5=8F=97=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E4=BC=A0=E5=85=A5=E7=9A=84moveableOptions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/stage/src/StageCore.ts | 22 ++++--- packages/stage/src/StageHighlight.ts | 2 +- packages/stage/src/StageMask.ts | 14 ++-- packages/stage/src/StageMultiDragResize.ts | 76 +++++++++++++++++----- packages/stage/src/TargetCalibrate.ts | 3 +- 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/packages/stage/src/StageCore.ts b/packages/stage/src/StageCore.ts index da71a8d5..b2e7cdb6 100644 --- a/packages/stage/src/StageCore.ts +++ b/packages/stage/src/StageCore.ts @@ -99,7 +99,7 @@ export default class StageCore extends EventEmitter { this.emit('changeGuides', data); }) .on('highlight', async (event: MouseEvent) => { - const el = await this.getElementFromPoint(event, 'mousemove'); + const el = await this.getElementFromPoint(event); if (!el) return; await this.highlight(el); if (this.highlightedDom === this.selectedDom) { @@ -114,8 +114,6 @@ export default class StageCore extends EventEmitter { .on('beforeMultiSelect', async (event: MouseEvent) => { const el = await this.getElementFromPoint(event); if (!el) return; - // 多选不可以选中magic-ui-page - if (el.className.includes(PAGE_CLASS)) return; this.clearSelectStatus('select'); // 如果已有单选选中元素,不是magic-ui-page就可以加入多选列表 if (this.selectedDom && !this.selectedDom.className.includes(PAGE_CLASS)) { @@ -166,21 +164,29 @@ export default class StageCore extends EventEmitter { return doc?.elementsFromPoint(x / zoom, y / zoom) as HTMLElement[]; } - public async getElementFromPoint(event: MouseEvent, type?: String) { + public async getElementFromPoint(event: MouseEvent) { const els = this.getElementsFromPoint(event); let stopped = false; const stop = () => (stopped = true); for (const el of els) { - if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.canSelect(el, event, stop))) { + if (!el.id.startsWith(GHOST_EL_ID_PREFIX) && (await this.isElCanSelect(el, event, stop))) { if (stopped) break; - if (event.type === type) { - return el; - } return el; } } } + public async isElCanSelect(el: HTMLElement, event: MouseEvent, stop: () => boolean): Promise { + // 执行业务方传入的判断逻辑 + const canSelectByProp = await this.canSelect(el, event, stop); + if (!canSelectByProp) return false; + // 多选规则 + if (this.mask.isMultiSelectStatus) { + return this.multiDr.canSelect(el); + } + return true; + } + /** * 选中组件 * @param idOrEl 组件Dom节点的id属性,或者Dom节点 diff --git a/packages/stage/src/StageHighlight.ts b/packages/stage/src/StageHighlight.ts index 943030c8..1426eeea 100644 --- a/packages/stage/src/StageHighlight.ts +++ b/packages/stage/src/StageHighlight.ts @@ -57,7 +57,7 @@ export default class StageHighlight extends EventEmitter { target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX), origin: false, rootContainer: this.core.container, - zoom: 1, + zoom: 2, }); } diff --git a/packages/stage/src/StageMask.ts b/packages/stage/src/StageMask.ts index 88b7dfee..fb7f4ad7 100644 --- a/packages/stage/src/StageMask.ts +++ b/packages/stage/src/StageMask.ts @@ -83,7 +83,7 @@ export default class StageMask extends Rule { public maxScrollTop = 0; public maxScrollLeft = 0; public intersectionObserver: IntersectionObserver | null = null; - public shiftKeyDown: Boolean = false; + public isMultiSelectStatus: Boolean = false; private mode: Mode = Mode.ABSOLUTE; private pageResizeObserver: ResizeObserver | null = null; @@ -110,11 +110,11 @@ export default class StageMask extends Rule { this.content.addEventListener('mouseleave', this.mouseLeaveHandler); KeyController.global.keydown('shift', (e) => { e.inputEvent.preventDefault(); - this.shiftKeyDown = true; + this.isMultiSelectStatus = true; }); KeyController.global.keyup('shift', (e) => { e.inputEvent.preventDefault(); - this.shiftKeyDown = false; + this.isMultiSelectStatus = false; }); } @@ -308,7 +308,7 @@ export default class StageMask extends Rule { if (event.button !== MouseButton.LEFT && event.button !== MouseButton.RIGHT) return; // 如果单击多选选中区域,则不需要再触发选中了,而可能是拖动行为 - if (!this.shiftKeyDown && (event.target as HTMLDivElement).className.indexOf('moveable-area') !== -1) { + if (!this.isMultiSelectStatus && (event.target as HTMLDivElement).className.indexOf('moveable-area') !== -1) { return; } // 点击对象如果是边框锚点,则可能是resize @@ -319,13 +319,13 @@ export default class StageMask extends Rule { this.content.removeEventListener('mousemove', this.highlightHandler); // 判断触发多选还是单选 - if (this.shiftKeyDown) { + if (this.isMultiSelectStatus) { this.emit('beforeMultiSelect', event); } else { this.emit('beforeSelect', event); - // 如果是右键点击,这里的mouseup事件监听没有效果 - globalThis.document.addEventListener('mouseup', this.mouseUpHandler); } + // 如果是右键点击,这里的mouseup事件监听没有效果 + globalThis.document.addEventListener('mouseup', this.mouseUpHandler); }; private mouseUpHandler = (): void => { diff --git a/packages/stage/src/StageMultiDragResize.ts b/packages/stage/src/StageMultiDragResize.ts index 6d66ee89..27540ee2 100644 --- a/packages/stage/src/StageMultiDragResize.ts +++ b/packages/stage/src/StageMultiDragResize.ts @@ -18,14 +18,15 @@ import { EventEmitter } from 'events'; +import type { MoveableOptions } from 'moveable'; import Moveable from 'moveable'; import MoveableHelper from 'moveable-helper'; -import { DRAG_EL_ID_PREFIX } from './const'; +import { DRAG_EL_ID_PREFIX, PAGE_CLASS } from './const'; import StageCore from './StageCore'; import StageMask from './StageMask'; import { StageDragResizeConfig } from './types'; -import { calcValueByFontsize, getTargetElStyle } from './util'; +import { calcValueByFontsize, getMode, getTargetElStyle } from './util'; export default class StageMultiDragResize extends EventEmitter { public core: StageCore; public mask: StageMask; @@ -70,19 +71,12 @@ export default class StageMultiDragResize extends EventEmitter { this.moveableForMulti?.destroy(); this.multiMoveableHelper?.clear(); - this.moveableForMulti = new Moveable(this.container, { - target: this.dragElList, - defaultGroupRotate: 0, - defaultGroupOrigin: '50% 50%', - draggable: true, - resizable: true, - throttleDrag: 0, - startDragRotate: 0, - throttleDragRotate: 0, - zoom: 1, - origin: true, - padding: { left: 0, top: 0, right: 0, bottom: 0 }, - }); + this.moveableForMulti = new Moveable( + this.container, + this.getOptions({ + target: this.dragElList, + }), + ); this.multiMoveableHelper = MoveableHelper.create({ useBeforeRender: true, useRender: false, @@ -134,6 +128,30 @@ export default class StageMultiDragResize extends EventEmitter { }); } + public canSelect(el: HTMLElement): Boolean { + // 多选不可以选中magic-ui-page + if (el.className.includes(PAGE_CLASS)) { + this.core.highlightedDom = undefined; + this.core.highlightLayer.clearHighlight(); + return false; + } + const currentTargetMode = getMode(el); + let selectedDomMode = ''; + + if (this.targetList.length === 0 && this.core.selectedDom) { + // 单选后添加到多选的情况 + selectedDomMode = getMode(this.core.selectedDom); + } else if (this.targetList.length > 0) { + // 已加入多选列表的布局模式是一样的,取第一个判断 + selectedDomMode = getMode(this.targetList[0]); + } + // 定位模式不同,不可混选 + if (currentTargetMode !== selectedDomMode) { + return false; + } + return true; + } + /** * 清除多选状态 */ @@ -142,6 +160,7 @@ export default class StageMultiDragResize extends EventEmitter { this.destroyDragElList(); this.moveableForMulti.target = null; this.moveableForMulti.updateTarget(); + this.targetList = []; } /** @@ -182,4 +201,31 @@ export default class StageMultiDragResize extends EventEmitter { }); }); } + + /** + * 获取moveable options参数 + * @param {MoveableOptions} options + * @return {MoveableOptions} moveable options参数 + */ + private getOptions(options: MoveableOptions = {}): MoveableOptions { + let { moveableOptions = {} } = this.core.config; + + if (typeof moveableOptions === 'function') { + moveableOptions = moveableOptions(this.core); + } + return { + defaultGroupRotate: 0, + defaultGroupOrigin: '50% 50%', + draggable: true, + resizable: true, + throttleDrag: 0, + startDragRotate: 0, + throttleDragRotate: 0, + zoom: 1, + origin: true, + padding: { left: 0, top: 0, right: 0, bottom: 0 }, + ...options, + ...moveableOptions, + }; + } } diff --git a/packages/stage/src/TargetCalibrate.ts b/packages/stage/src/TargetCalibrate.ts index b6efa2c3..21f5624c 100644 --- a/packages/stage/src/TargetCalibrate.ts +++ b/packages/stage/src/TargetCalibrate.ts @@ -18,7 +18,7 @@ import { EventEmitter } from 'events'; -import { Mode } from './const'; +import { Mode, ZIndex } from './const'; import StageCore from './StageCore'; import StageDragResize from './StageDragResize'; import StageMask from './StageMask'; @@ -57,6 +57,7 @@ export default class TargetCalibrate extends EventEmitter { top: ${top}px; width: ${el.clientWidth}px; height: ${el.clientHeight}px; + z-index: ${ZIndex.DRAG_EL}; `; this.operationEl.id = `${prefix}${el.id}`;