mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
parent
e3af0b2914
commit
449efcc56b
@ -25,6 +25,7 @@ import { Id } from '@tmagic/schema';
|
||||
import { addClassName, getDocument, removeClassNameByClassName } from '@tmagic/utils';
|
||||
|
||||
import { CONTAINER_HIGHLIGHT_CLASS_NAME, GHOST_EL_ID_PREFIX, GuidesType, MouseButton, PAGE_CLASS } from './const';
|
||||
import DragResizeHelper from './DragResizeHelper';
|
||||
import StageDragResize from './StageDragResize';
|
||||
import StageHighlight from './StageHighlight';
|
||||
import StageMultiDragResize from './StageMultiDragResize';
|
||||
@ -101,27 +102,30 @@ export default class ActionManager extends EventEmitter {
|
||||
this.getRenderDocument = config.getRenderDocument;
|
||||
this.isContainer = config.isContainer;
|
||||
|
||||
const createDrHelper = () =>
|
||||
new DragResizeHelper({
|
||||
container: config.container,
|
||||
updateDragEl: config.updateDragEl,
|
||||
});
|
||||
|
||||
this.dr = new StageDragResize({
|
||||
container: config.container,
|
||||
disabledDragStart: config.disabledDragStart,
|
||||
moveableOptions: this.changeCallback(config.moveableOptions),
|
||||
dragResizeHelper: createDrHelper(),
|
||||
getRootContainer: config.getRootContainer,
|
||||
getRenderDocument: config.getRenderDocument,
|
||||
updateDragEl: config.updateDragEl,
|
||||
markContainerEnd: () => this.markContainerEnd(),
|
||||
delayedMarkContainer: (event: MouseEvent, exclude: Element[]) => {
|
||||
if (this.canAddToContainer()) {
|
||||
return this.delayedMarkContainer(event, exclude);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
moveableOptions: this.changeCallback(config.moveableOptions),
|
||||
markContainerEnd: this.markContainerEnd.bind(this),
|
||||
delayedMarkContainer: this.delayedMarkContainer.bind(this),
|
||||
});
|
||||
this.multiDr = new StageMultiDragResize({
|
||||
container: config.container,
|
||||
multiMoveableOptions: config.multiMoveableOptions,
|
||||
dragResizeHelper: createDrHelper(),
|
||||
getRootContainer: config.getRootContainer,
|
||||
getRenderDocument: config.getRenderDocument,
|
||||
updateDragEl: config.updateDragEl,
|
||||
markContainerEnd: this.markContainerEnd.bind(this),
|
||||
delayedMarkContainer: this.delayedMarkContainer.bind(this),
|
||||
});
|
||||
this.highlightLayer = new StageHighlight({
|
||||
container: config.container,
|
||||
@ -320,10 +324,13 @@ export default class ActionManager extends EventEmitter {
|
||||
* @param excludeElList 计算鼠标所在容器时要排除的元素列表
|
||||
* @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
|
||||
*/
|
||||
public delayedMarkContainer(event: MouseEvent, excludeElList: Element[] = []): NodeJS.Timeout {
|
||||
return globalThis.setTimeout(() => {
|
||||
this.addContainerHighlightClassName(event, excludeElList);
|
||||
}, this.containerHighlightDuration);
|
||||
public delayedMarkContainer(event: MouseEvent, excludeElList: Element[] = []): NodeJS.Timeout | undefined {
|
||||
if (this.canAddToContainer()) {
|
||||
return globalThis.setTimeout(() => {
|
||||
this.addContainerHighlightClassName(event, excludeElList);
|
||||
}, this.containerHighlightDuration);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
@ -466,8 +473,8 @@ export default class ActionManager extends EventEmitter {
|
||||
});
|
||||
|
||||
this.multiDr
|
||||
.on('update', (data: UpdateEventData, parentEl: HTMLElement | null) => {
|
||||
this.emit('multi-update', data, parentEl);
|
||||
.on('update', (data: UpdateEventData) => {
|
||||
this.emit('multi-update', data);
|
||||
})
|
||||
.on('change-to-select', async (id: Id) => {
|
||||
// 如果还在多选状态,不触发切换到单选
|
||||
|
@ -34,8 +34,8 @@ import MoveableHelper from 'moveable-helper';
|
||||
|
||||
import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, Mode, ZIndex } from './const';
|
||||
import TargetShadow from './TargetShadow';
|
||||
import { DragResizeHelperConfig, TargetElement } from './types';
|
||||
import { getAbsolutePosition, getOffset } from './util';
|
||||
import { DragResizeHelperConfig, Rect, TargetElement } from './types';
|
||||
import { calcValueByFontsize, getAbsolutePosition, getOffset } from './util';
|
||||
|
||||
/**
|
||||
* 拖拽/改变大小等操作发生时,moveable会抛出各种状态事件,DragResizeHelper负责响应这些事件,对目标节点target和拖拽节点targetShadow进行修改;
|
||||
@ -282,6 +282,44 @@ export default class DragResizeHelper {
|
||||
this.moveableHelper.onDragGroup(e);
|
||||
}
|
||||
|
||||
public getUpdatedElRect(el: HTMLElement, parentEl: HTMLElement | null, doc: Document): Rect {
|
||||
const offset = this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : { left: el.offsetLeft, top: el.offsetTop };
|
||||
|
||||
let left = calcValueByFontsize(doc, offset.left);
|
||||
let top = calcValueByFontsize(doc, offset.top);
|
||||
const width = calcValueByFontsize(doc, el.clientWidth);
|
||||
const height = calcValueByFontsize(doc, el.clientHeight);
|
||||
|
||||
let shadowEl = this.getShadowEl();
|
||||
const shadowEls = this.getShadowEls();
|
||||
|
||||
if (shadowEls.length) {
|
||||
shadowEl = shadowEls.find((item) => item.id.endsWith(el.id));
|
||||
}
|
||||
|
||||
if (parentEl && this.mode === Mode.ABSOLUTE && shadowEl) {
|
||||
const targetShadowHtmlEl = shadowEl as HTMLElement;
|
||||
const targetShadowElOffsetLeft = targetShadowHtmlEl.offsetLeft || 0;
|
||||
const targetShadowElOffsetTop = targetShadowHtmlEl.offsetTop || 0;
|
||||
|
||||
const frame = this.getFrame(shadowEl);
|
||||
|
||||
const [translateX, translateY] = frame?.properties.transform.translate.value;
|
||||
const { left: parentLeft, top: parentTop } = getOffset(parentEl);
|
||||
|
||||
left =
|
||||
calcValueByFontsize(doc, targetShadowElOffsetLeft) +
|
||||
parseFloat(translateX) -
|
||||
calcValueByFontsize(doc, parentLeft);
|
||||
top =
|
||||
calcValueByFontsize(doc, targetShadowElOffsetTop) +
|
||||
parseFloat(translateY) -
|
||||
calcValueByFontsize(doc, parentTop);
|
||||
}
|
||||
|
||||
return { width, height, left, top };
|
||||
}
|
||||
|
||||
/**
|
||||
* 多选状态设置多个节点的快照
|
||||
*/
|
||||
|
@ -189,7 +189,10 @@ export default class StageCore extends EventEmitter {
|
||||
/**
|
||||
* @deprecated 废弃接口,建议用delayedMarkContainer代替
|
||||
*/
|
||||
public getAddContainerHighlightClassNameTimeout(event: MouseEvent, excludeElList: Element[] = []): NodeJS.Timeout {
|
||||
public getAddContainerHighlightClassNameTimeout(
|
||||
event: MouseEvent,
|
||||
excludeElList: Element[] = [],
|
||||
): NodeJS.Timeout | undefined {
|
||||
return this.delayedMarkContainer(event, excludeElList);
|
||||
}
|
||||
|
||||
@ -200,7 +203,7 @@ export default class StageCore extends EventEmitter {
|
||||
* @param excludeElList 计算鼠标所在容器时要排除的元素列表
|
||||
* @returns timeoutId,调用方在鼠标移走时要取消该timeout,阻止标记
|
||||
*/
|
||||
public delayedMarkContainer(event: MouseEvent, excludeElList: Element[] = []): NodeJS.Timeout {
|
||||
public delayedMarkContainer(event: MouseEvent, excludeElList: Element[] = []): NodeJS.Timeout | undefined {
|
||||
return this.actionManager.delayedMarkContainer(event, excludeElList);
|
||||
}
|
||||
|
||||
@ -331,8 +334,8 @@ export default class StageCore extends EventEmitter {
|
||||
// 先保证画布内完成渲染,再通知外部更新
|
||||
setTimeout(() => this.emit('select', el));
|
||||
})
|
||||
.on('multi-update', (data: UpdateEventData, parentEl: HTMLElement | null) => {
|
||||
this.emit('update', { data, parentEl });
|
||||
.on('multi-update', (data: UpdateEventData) => {
|
||||
this.emit('update', data);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ import DragResizeHelper from './DragResizeHelper';
|
||||
import MoveableOptionsManager from './MoveableOptionsManager';
|
||||
import type { DelayedMarkContainer, GetRenderDocument, MarkContainerEnd, StageDragResizeConfig } from './types';
|
||||
import { StageDragStatus } from './types';
|
||||
import { calcValueByFontsize, down, getMode, getOffset, up } from './util';
|
||||
import { down, getMode, up } from './util';
|
||||
|
||||
/**
|
||||
* 管理单选操作,响应选中操作,初始化moveableOption参数并初始化moveable,处理moveable回调事件对组件进行更新
|
||||
@ -51,10 +51,7 @@ export default class StageDragResize extends MoveableOptionsManager {
|
||||
this.delayedMarkContainer = config.delayedMarkContainer;
|
||||
this.disabledDragStart = config.disabledDragStart;
|
||||
|
||||
this.dragResizeHelper = new DragResizeHelper({
|
||||
container: config.container,
|
||||
updateDragEl: config.updateDragEl,
|
||||
});
|
||||
this.dragResizeHelper = config.dragResizeHelper;
|
||||
|
||||
this.on('update-moveable', () => {
|
||||
if (this.moveable) {
|
||||
@ -315,40 +312,13 @@ export default class StageDragResize extends MoveableOptionsManager {
|
||||
|
||||
if (!doc) return;
|
||||
|
||||
const offset =
|
||||
this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : { left: this.target.offsetLeft, top: this.target.offsetTop };
|
||||
|
||||
let left = calcValueByFontsize(doc, offset.left);
|
||||
let top = calcValueByFontsize(doc, offset.top);
|
||||
const width = calcValueByFontsize(doc, this.target.clientWidth);
|
||||
const height = calcValueByFontsize(doc, this.target.clientHeight);
|
||||
|
||||
const shadowEl = this.dragResizeHelper.getShadowEl();
|
||||
if (parentEl && this.mode === Mode.ABSOLUTE && shadowEl) {
|
||||
const targetShadowHtmlEl = shadowEl as HTMLElement;
|
||||
const targetShadowElOffsetLeft = targetShadowHtmlEl.offsetLeft || 0;
|
||||
const targetShadowElOffsetTop = targetShadowHtmlEl.offsetTop || 0;
|
||||
|
||||
const frame = this.dragResizeHelper.getFrame(shadowEl);
|
||||
|
||||
const [translateX, translateY] = frame?.properties.transform.translate.value;
|
||||
const { left: parentLeft, top: parentTop } = getOffset(parentEl);
|
||||
|
||||
left =
|
||||
calcValueByFontsize(doc, targetShadowElOffsetLeft) +
|
||||
parseFloat(translateX) -
|
||||
calcValueByFontsize(doc, parentLeft);
|
||||
top =
|
||||
calcValueByFontsize(doc, targetShadowElOffsetTop) +
|
||||
parseFloat(translateY) -
|
||||
calcValueByFontsize(doc, parentTop);
|
||||
}
|
||||
const rect = this.dragResizeHelper.getUpdatedElRect(this.target, parentEl, doc);
|
||||
|
||||
this.emit('update', {
|
||||
data: [
|
||||
{
|
||||
el: this.target,
|
||||
style: isResize ? { left, top, width, height } : { left, top },
|
||||
style: isResize ? rect : { left: rect.left, top: rect.top },
|
||||
},
|
||||
],
|
||||
parentEl,
|
||||
|
@ -21,8 +21,15 @@ import Moveable from 'moveable';
|
||||
import { DRAG_EL_ID_PREFIX, Mode } from './const';
|
||||
import DragResizeHelper from './DragResizeHelper';
|
||||
import MoveableOptionsManager from './MoveableOptionsManager';
|
||||
import { GetRenderDocument, MoveableOptionsManagerConfig, StageDragStatus, StageMultiDragResizeConfig } from './types';
|
||||
import { calcValueByFontsize, getMode } from './util';
|
||||
import {
|
||||
DelayedMarkContainer,
|
||||
GetRenderDocument,
|
||||
MarkContainerEnd,
|
||||
MoveableOptionsManagerConfig,
|
||||
StageDragStatus,
|
||||
StageMultiDragResizeConfig,
|
||||
} from './types';
|
||||
import { getMode } from './util';
|
||||
|
||||
export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
/** 画布容器 */
|
||||
@ -34,6 +41,8 @@ export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
public dragStatus: StageDragStatus = StageDragStatus.END;
|
||||
private dragResizeHelper: DragResizeHelper;
|
||||
private getRenderDocument: GetRenderDocument;
|
||||
private delayedMarkContainer: DelayedMarkContainer;
|
||||
private markContainerEnd: MarkContainerEnd;
|
||||
|
||||
constructor(config: StageMultiDragResizeConfig) {
|
||||
const moveableOptionsManagerConfig: MoveableOptionsManagerConfig = {
|
||||
@ -43,13 +52,12 @@ export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
};
|
||||
super(moveableOptionsManagerConfig);
|
||||
|
||||
this.delayedMarkContainer = config.delayedMarkContainer;
|
||||
this.markContainerEnd = config.markContainerEnd;
|
||||
this.container = config.container;
|
||||
this.getRenderDocument = config.getRenderDocument;
|
||||
|
||||
this.dragResizeHelper = new DragResizeHelper({
|
||||
container: config.container,
|
||||
updateDragEl: config.updateDragEl,
|
||||
});
|
||||
this.dragResizeHelper = config.dragResizeHelper;
|
||||
|
||||
this.on('update-moveable', () => {
|
||||
if (this.moveableForMulti) {
|
||||
@ -85,6 +93,8 @@ export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
}),
|
||||
);
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined;
|
||||
|
||||
this.moveableForMulti
|
||||
.on('resizeGroupStart', (e) => {
|
||||
this.dragResizeHelper.onResizeGroupStart(e);
|
||||
@ -103,11 +113,18 @@ export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
this.dragStatus = StageDragStatus.START;
|
||||
})
|
||||
.on('dragGroup', (e) => {
|
||||
if (timeout) {
|
||||
globalThis.clearTimeout(timeout);
|
||||
timeout = undefined;
|
||||
}
|
||||
timeout = this.delayedMarkContainer(e.inputEvent, this.targetList);
|
||||
|
||||
this.dragResizeHelper.onDragGroup(e);
|
||||
this.dragStatus = StageDragStatus.ING;
|
||||
})
|
||||
.on('dragGroupEnd', () => {
|
||||
this.update();
|
||||
const parentEl = this.markContainerEnd();
|
||||
this.update(false, parentEl);
|
||||
this.dragStatus = StageDragStatus.END;
|
||||
})
|
||||
.on('clickGroup', (e) => {
|
||||
@ -182,22 +199,19 @@ export default class StageMultiDragResize extends MoveableOptionsManager {
|
||||
* 拖拽完成后将更新的位置信息暴露给上层业务方,业务方可以接收事件进行保存
|
||||
* @param isResize 是否进行大小缩放
|
||||
*/
|
||||
private update(isResize = false): void {
|
||||
private update(isResize = false, parentEl: HTMLElement | null = null): void {
|
||||
if (this.targetList.length === 0) return;
|
||||
|
||||
const doc = this.getRenderDocument();
|
||||
if (!doc) return;
|
||||
|
||||
const data = this.targetList.map((targetItem) => {
|
||||
const left = calcValueByFontsize(doc, targetItem.offsetLeft);
|
||||
const top = calcValueByFontsize(doc, targetItem.offsetTop);
|
||||
const width = calcValueByFontsize(doc, targetItem.clientWidth);
|
||||
const height = calcValueByFontsize(doc, targetItem.clientHeight);
|
||||
const rect = this.dragResizeHelper.getUpdatedElRect(targetItem, parentEl, doc);
|
||||
return {
|
||||
el: targetItem,
|
||||
style: isResize ? { left, top, width, height } : { left, top },
|
||||
style: isResize ? rect : { left: rect.left, top: rect.top },
|
||||
};
|
||||
});
|
||||
this.emit('update', data, null);
|
||||
this.emit('update', { data, parentEl });
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import Core from '@tmagic/core';
|
||||
import type { Id, MApp, MContainer, MNode } from '@tmagic/schema';
|
||||
|
||||
import { GuidesType, ZIndex } from './const';
|
||||
import DragResizeHelper from './DragResizeHelper';
|
||||
import StageCore from './StageCore';
|
||||
|
||||
export type TargetElement = HTMLElement | SVGElement;
|
||||
@ -112,21 +113,23 @@ export interface StageMaskConfig {
|
||||
|
||||
export interface StageDragResizeConfig {
|
||||
container: HTMLElement;
|
||||
dragResizeHelper: DragResizeHelper;
|
||||
moveableOptions?: CustomizeMoveableOptions;
|
||||
disabledDragStart?: boolean;
|
||||
getRootContainer: GetRootContainer;
|
||||
getRenderDocument: GetRenderDocument;
|
||||
markContainerEnd: MarkContainerEnd;
|
||||
delayedMarkContainer: DelayedMarkContainer;
|
||||
updateDragEl?: UpdateDragEl;
|
||||
}
|
||||
|
||||
export interface StageMultiDragResizeConfig {
|
||||
container: HTMLElement;
|
||||
dragResizeHelper: DragResizeHelper;
|
||||
multiMoveableOptions?: CustomizeMoveableOptions;
|
||||
getRootContainer: GetRootContainer;
|
||||
getRenderDocument: GetRenderDocument;
|
||||
updateDragEl?: UpdateDragEl;
|
||||
markContainerEnd: MarkContainerEnd;
|
||||
delayedMarkContainer: DelayedMarkContainer;
|
||||
}
|
||||
|
||||
export interface DragResizeHelperConfig {
|
||||
|
Loading…
x
Reference in New Issue
Block a user